@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,449 +0,0 @@
1
- /**
2
- * AnnouncementQueue 测试
3
- */
4
-
5
- import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
6
- import { AnnouncementQueue } from './announcement-queue.js';
7
- import type { TaskAnnouncement, TaskClaim } from './types.js';
8
-
9
- describe('AnnouncementQueue', () => {
10
- let queue: AnnouncementQueue;
11
-
12
- beforeEach(() => {
13
- queue = new AnnouncementQueue({ maxSize: 10, maxAgeMs: 1000 });
14
- });
15
-
16
- afterEach(() => {
17
- queue.clear();
18
- });
19
-
20
- describe('create()', () => {
21
- it('应该创建任务广播', () => {
22
- const announcement = queue.create({
23
- taskType: 'test',
24
- description: 'Test task',
25
- timeout: 5000,
26
- from: 'peer-1'
27
- });
28
-
29
- expect(announcement.announcementId).toMatch(/^ann-/);
30
- expect(announcement.taskType).toBe('test');
31
- expect(announcement.description).toBe('Test task');
32
- expect(announcement.status).toBe('open');
33
- expect(announcement.from).toBe('peer-1');
34
- expect(announcement.claims).toEqual([]);
35
- expect(announcement.timestamp).toBeGreaterThan(0);
36
- });
37
-
38
- it('应该在队列满时抛出错误', () => {
39
- const smallQueue = new AnnouncementQueue({ maxSize: 2 });
40
-
41
- smallQueue.create({ taskType: 'test', description: 'task 1', timeout: 5000, from: 'peer-1' });
42
- smallQueue.create({ taskType: 'test', description: 'task 2', timeout: 5000, from: 'peer-1' });
43
-
44
- expect(() => smallQueue.create({ taskType: 'test', description: 'task 3', timeout: 5000, from: 'peer-1' }))
45
- .toThrow('Announcement queue is full');
46
- });
47
-
48
- it('应该为每个广播生成唯一 ID', () => {
49
- const a1 = queue.create({ taskType: 'test', description: 'task 1', timeout: 5000, from: 'peer-1' });
50
- const a2 = queue.create({ taskType: 'test', description: 'task 2', timeout: 5000, from: 'peer-1' });
51
-
52
- expect(a1.announcementId).not.toBe(a2.announcementId);
53
- });
54
- });
55
-
56
- describe('get() 和 getOpen()', () => {
57
- it('应该获取特定广播', () => {
58
- const created = queue.create({ taskType: 'test', description: 'Test', timeout: 5000, from: 'peer-1' });
59
- const fetched = queue.get(created.announcementId);
60
-
61
- expect(fetched).toBeDefined();
62
- expect(fetched?.announcementId).toBe(created.announcementId);
63
- });
64
-
65
- it('应该对不存在的广播返回 undefined', () => {
66
- expect(queue.get('non-existent')).toBeUndefined();
67
- });
68
-
69
- it('应该获取所有开放的广播', () => {
70
- queue.create({ taskType: 'test', description: 'task 1', timeout: 5000, from: 'peer-1' });
71
- queue.create({ taskType: 'test', description: 'task 2', timeout: 5000, from: 'peer-1' });
72
-
73
- const open = queue.getOpen();
74
- expect(open).toHaveLength(2);
75
- expect(open.every(a => a.status === 'open')).toBe(true);
76
- });
77
-
78
- it('应该按时间排序返回开放广播', async () => {
79
- queue.create({ taskType: 'test', description: 'first', timeout: 5000, from: 'peer-1' });
80
- await new Promise(r => setTimeout(r, 10));
81
- queue.create({ taskType: 'test', description: 'second', timeout: 5000, from: 'peer-1' });
82
-
83
- const open = queue.getOpen();
84
- expect(open[0].description).toBe('first');
85
- expect(open[1].description).toBe('second');
86
- });
87
- });
88
-
89
- describe('submitClaim()', () => {
90
- it('应该提交认领', () => {
91
- const announcement = queue.create({ taskType: 'test', description: 'Test', timeout: 5000, from: 'peer-1' });
92
- const claim = queue.submitClaim(announcement.announcementId, {
93
- claimant: 'peer-2',
94
- claimantName: 'Worker 2',
95
- confidence: 0.9
96
- });
97
-
98
- expect(claim).toBeDefined();
99
- expect(claim?.claimId).toMatch(/^claim-/);
100
- expect(claim?.claimant).toBe('peer-2');
101
- expect(claim?.status).toBe('pending');
102
- expect(claim?.announcementId).toBe(announcement.announcementId);
103
- });
104
-
105
- it('应该对不存在的广播返回 null', () => {
106
- const claim = queue.submitClaim('non-existent', {
107
- claimant: 'peer-2'
108
- });
109
- expect(claim).toBeNull();
110
- });
111
-
112
- it('应该对非开放状态的广播返回 null', () => {
113
- const announcement = queue.create({ taskType: 'test', description: 'Test', timeout: 5000, from: 'peer-1' });
114
- queue.markDelegated(announcement.announcementId);
115
-
116
- const claim = queue.submitClaim(announcement.announcementId, {
117
- claimant: 'peer-2'
118
- });
119
- expect(claim).toBeNull();
120
- });
121
-
122
- it('应该防止重复认领', () => {
123
- const announcement = queue.create({ taskType: 'test', description: 'Test', timeout: 5000, from: 'peer-1' });
124
-
125
- const claim1 = queue.submitClaim(announcement.announcementId, {
126
- claimant: 'peer-2',
127
- confidence: 0.8
128
- });
129
-
130
- const claim2 = queue.submitClaim(announcement.announcementId, {
131
- claimant: 'peer-2',
132
- confidence: 0.9 // 不同的 confidence
133
- });
134
-
135
- // 应该返回同一个认领
136
- expect(claim1?.claimId).toBe(claim2?.claimId);
137
- expect(announcement.claims).toHaveLength(1);
138
- });
139
-
140
- it('应该允许不同用户认领同一广播', () => {
141
- const announcement = queue.create({ taskType: 'test', description: 'Test', timeout: 5000, from: 'peer-1' });
142
-
143
- const claim1 = queue.submitClaim(announcement.announcementId, { claimant: 'peer-2' });
144
- const claim2 = queue.submitClaim(announcement.announcementId, { claimant: 'peer-3' });
145
-
146
- expect(claim1?.claimId).not.toBe(claim2?.claimId);
147
- expect(announcement.claims).toHaveLength(2);
148
- });
149
- });
150
-
151
- describe('acceptClaim() 和 rejectClaim()', () => {
152
- it('应该接受认领', () => {
153
- const announcement = queue.create({ taskType: 'test', description: 'Test', timeout: 5000, from: 'peer-1' });
154
- const claim = queue.submitClaim(announcement.announcementId, { claimant: 'peer-2' });
155
-
156
- const accepted = queue.acceptClaim(announcement.announcementId, claim!.claimId);
157
-
158
- expect(accepted?.status).toBe('accepted');
159
- expect(announcement.status).toBe('claimed');
160
- });
161
-
162
- it('接受认领时应该拒绝其他认领', () => {
163
- const announcement = queue.create({ taskType: 'test', description: 'Test', timeout: 5000, from: 'peer-1' });
164
- const claim1 = queue.submitClaim(announcement.announcementId, { claimant: 'peer-2' });
165
- const claim2 = queue.submitClaim(announcement.announcementId, { claimant: 'peer-3' });
166
-
167
- queue.acceptClaim(announcement.announcementId, claim1!.claimId);
168
-
169
- expect(claim1?.status).toBe('accepted');
170
- expect(claim2?.status).toBe('rejected');
171
- });
172
-
173
- it('应该拒绝认领', () => {
174
- const announcement = queue.create({ taskType: 'test', description: 'Test', timeout: 5000, from: 'peer-1' });
175
- const claim = queue.submitClaim(announcement.announcementId, { claimant: 'peer-2' });
176
-
177
- const rejected = queue.rejectClaim(announcement.announcementId, claim!.claimId);
178
-
179
- expect(rejected?.status).toBe('rejected');
180
- expect(announcement.status).toBe('open'); // 广播仍然开放
181
- });
182
-
183
- it('应该对不存在的广播返回 null', () => {
184
- const result = queue.acceptClaim('non-existent', 'claim-1');
185
- expect(result).toBeNull();
186
- });
187
-
188
- it('应该对不存在的认领返回 null', () => {
189
- const announcement = queue.create({ taskType: 'test', description: 'Test', timeout: 5000, from: 'peer-1' });
190
- const result = queue.acceptClaim(announcement.announcementId, 'non-existent-claim');
191
- expect(result).toBeNull();
192
- });
193
- });
194
-
195
- describe('getMyClaims() 和 getMyAnnouncements()', () => {
196
- it('应该获取我的认领', () => {
197
- const a1 = queue.create({ taskType: 'test', description: 'Task 1', timeout: 5000, from: 'peer-1' });
198
- const a2 = queue.create({ taskType: 'test', description: 'Task 2', timeout: 5000, from: 'peer-1' });
199
-
200
- queue.submitClaim(a1.announcementId, { claimant: 'peer-2' });
201
- queue.submitClaim(a2.announcementId, { claimant: 'peer-2' });
202
- queue.submitClaim(a1.announcementId, { claimant: 'peer-3' });
203
-
204
- const myClaims = queue.getMyClaims('peer-2');
205
- expect(myClaims).toHaveLength(2);
206
- expect(myClaims.every(c => c.claimant === 'peer-2')).toBe(true);
207
- });
208
-
209
- it('应该获取我的广播', () => {
210
- queue.create({ taskType: 'test', description: 'Task 1', timeout: 5000, from: 'peer-1' });
211
- queue.create({ taskType: 'test', description: 'Task 2', timeout: 5000, from: 'peer-1' });
212
- queue.create({ taskType: 'test', description: 'Task 3', timeout: 5000, from: 'peer-2' });
213
-
214
- const myAnnouncements = queue.getMyAnnouncements('peer-1');
215
- expect(myAnnouncements).toHaveLength(2);
216
- expect(myAnnouncements.every(a => a.from === 'peer-1')).toBe(true);
217
- });
218
-
219
- it('应该按时间倒序返回', async () => {
220
- queue.create({ taskType: 'test', description: 'first', timeout: 5000, from: 'peer-1' });
221
- await new Promise(r => setTimeout(r, 10));
222
- queue.create({ taskType: 'test', description: 'second', timeout: 5000, from: 'peer-1' });
223
-
224
- const myAnnouncements = queue.getMyAnnouncements('peer-1');
225
- expect(myAnnouncements[0].description).toBe('second');
226
- expect(myAnnouncements[1].description).toBe('first');
227
- });
228
- });
229
-
230
- describe('markDelegated()', () => {
231
- it('应该标记为已委托', () => {
232
- const announcement = queue.create({ taskType: 'test', description: 'Test', timeout: 5000, from: 'peer-1' });
233
- const result = queue.markDelegated(announcement.announcementId);
234
-
235
- expect(result).toBe(true);
236
- expect(announcement.status).toBe('delegated');
237
- });
238
-
239
- it('应该对不存在的广播返回 false', () => {
240
- expect(queue.markDelegated('non-existent')).toBe(false);
241
- });
242
- });
243
-
244
- describe('getStats()', () => {
245
- it('应该返回正确的统计', () => {
246
- const a1 = queue.create({ taskType: 'test', description: 'Task 1', timeout: 5000, from: 'peer-1' });
247
- const a2 = queue.create({ taskType: 'test', description: 'Task 2', timeout: 5000, from: 'peer-1' });
248
-
249
- const claim = queue.submitClaim(a1.announcementId, { claimant: 'peer-2' });
250
- queue.acceptClaim(a1.announcementId, claim!.claimId);
251
- queue.markDelegated(a2.announcementId);
252
-
253
- const stats = queue.getStats();
254
- expect(stats.open).toBe(0);
255
- expect(stats.claimed).toBe(1);
256
- expect(stats.delegated).toBe(1);
257
- expect(stats.total).toBe(2);
258
- });
259
- });
260
-
261
- describe('cleanup()', () => {
262
- it('应该将过期广播标记为 expired', async () => {
263
- const fastExpireQueue = new AnnouncementQueue({ maxSize: 10, maxAgeMs: 50 });
264
- fastExpireQueue.create({ taskType: 'test', description: 'Task', timeout: 5000, from: 'peer-1' });
265
-
266
- await new Promise(r => setTimeout(r, 60));
267
-
268
- // 通过创建新任务触发清理
269
- fastExpireQueue.create({ taskType: 'test', description: 'New task', timeout: 5000, from: 'peer-1' });
270
-
271
- const stats = fastExpireQueue.getStats();
272
- expect(stats.expired).toBe(1);
273
- });
274
-
275
- it('应该删除过期很久的广播', async () => {
276
- const fastExpireQueue = new AnnouncementQueue({ maxSize: 10, maxAgeMs: 50 });
277
- fastExpireQueue.create({ taskType: 'test', description: 'Task', timeout: 5000, from: 'peer-1' });
278
-
279
- await new Promise(r => setTimeout(r, 120));
280
-
281
- // 触发清理
282
- fastExpireQueue.create({ taskType: 'test', description: 'New task', timeout: 5000, from: 'peer-1' });
283
-
284
- const stats = fastExpireQueue.getStats();
285
- expect(stats.total).toBe(1); // 只有新任务
286
- });
287
- });
288
-
289
- describe('clear()', () => {
290
- it('应该清空队列', () => {
291
- queue.create({ taskType: 'test', description: 'Task 1', timeout: 5000, from: 'peer-1' });
292
- queue.create({ taskType: 'test', description: 'Task 2', timeout: 5000, from: 'peer-1' });
293
-
294
- queue.clear();
295
-
296
- expect(queue.getStats().total).toBe(0);
297
- expect(queue.getOpen()).toEqual([]);
298
- });
299
- });
300
-
301
- describe('边界条件', () => {
302
- it('应该处理空输入', () => {
303
- const announcement = queue.create({
304
- taskType: '',
305
- description: '',
306
- timeout: 0,
307
- from: ''
308
- });
309
-
310
- expect(announcement).toBeDefined();
311
- expect(announcement.taskType).toBe('');
312
- });
313
-
314
- it('应该处理特殊字符', () => {
315
- const announcement = queue.create({
316
- taskType: 'test-特殊字符-🎮',
317
- description: 'Test with special chars: <>&"\'',
318
- timeout: 5000,
319
- from: 'peer-1'
320
- });
321
-
322
- expect(announcement.taskType).toBe('test-特殊字符-🎮');
323
- expect(announcement.description).toBe('Test with special chars: <>&"\'');
324
- });
325
- });
326
-
327
- // P1 修复:新增边界和并发测试
328
- describe('并发和边界情况', () => {
329
- it('应该正确处理 acceptClaim 的并发调用', async () => {
330
- const announcement = queue.create({ taskType: 'test', description: 'Test', timeout: 5000, from: 'peer-1' });
331
- const claim1 = queue.submitClaim(announcement.announcementId, { claimant: 'peer-2' });
332
- const claim2 = queue.submitClaim(announcement.announcementId, { claimant: 'peer-3' });
333
-
334
- // 模拟并发调用(虽然 JS 是单线程,但可以测试锁逻辑)
335
- const results = await Promise.all([
336
- Promise.resolve(queue.acceptClaim(announcement.announcementId, claim1!.claimId)),
337
- Promise.resolve(queue.acceptClaim(announcement.announcementId, claim2!.claimId))
338
- ]);
339
-
340
- // 只有一个应该成功
341
- const successCount = results.filter(r => r !== null).length;
342
- expect(successCount).toBeLessThanOrEqual(1);
343
- });
344
-
345
- it('应该处理已认领广播的再次认领尝试', () => {
346
- const announcement = queue.create({ taskType: 'test', description: 'Test', timeout: 5000, from: 'peer-1' });
347
- const claim = queue.submitClaim(announcement.announcementId, { claimant: 'peer-2' });
348
- queue.acceptClaim(announcement.announcementId, claim!.claimId);
349
-
350
- // 广播已认领,新认领应该失败
351
- const newClaim = queue.submitClaim(announcement.announcementId, { claimant: 'peer-3' });
352
- expect(newClaim).toBeNull();
353
- });
354
-
355
- it('应该处理同一认领的重复接受', () => {
356
- const announcement = queue.create({ taskType: 'test', description: 'Test', timeout: 5000, from: 'peer-1' });
357
- const claim = queue.submitClaim(announcement.announcementId, { claimant: 'peer-2' });
358
-
359
- // 第一次接受
360
- const result1 = queue.acceptClaim(announcement.announcementId, claim!.claimId);
361
- expect(result1?.status).toBe('accepted');
362
-
363
- // 第二次接受应该失败(广播已不是 open)
364
- const result2 = queue.acceptClaim(announcement.announcementId, claim!.claimId);
365
- expect(result2).toBeNull();
366
- });
367
-
368
- it('应该正确处理大量认领', () => {
369
- const announcement = queue.create({ taskType: 'test', description: 'Test', timeout: 5000, from: 'peer-1' });
370
-
371
- // 添加 100 个认领
372
- for (let i = 0; i < 100; i++) {
373
- queue.submitClaim(announcement.announcementId, { claimant: `peer-${i}` });
374
- }
375
-
376
- expect(announcement.claims).toHaveLength(100);
377
-
378
- // 接受其中一个
379
- const claim = announcement.claims![50];
380
- const result = queue.acceptClaim(announcement.announcementId, claim.claimId);
381
-
382
- expect(result?.status).toBe('accepted');
383
- // 其他认领应该被拒绝
384
- const rejectedCount = announcement.claims!.filter(c => c.status === 'rejected').length;
385
- expect(rejectedCount).toBe(99);
386
- });
387
- });
388
-
389
- describe('forceClearOrphanLocks', () => {
390
- it('应该清除孤立锁', () => {
391
- // 直接创建一个有锁的场景
392
- const announcement = queue.create({ taskType: 'test', description: 'Test', timeout: 5000, from: 'peer-1' });
393
- const claim = queue.submitClaim(announcement.announcementId, { claimant: 'peer-2' });
394
-
395
- // 模拟锁未释放的情况(直接操作内部状态)
396
- // 注意:这是一个测试内部实现的测试
397
- expect(queue.hasOrphanLocks()).toBe(false);
398
-
399
- // 清除应该返回 0
400
- expect(queue.forceClearOrphanLocks()).toBe(0);
401
- });
402
-
403
- it('clear 应该同时清除锁', () => {
404
- queue.create({ taskType: 'test', description: 'Test', timeout: 5000, from: 'peer-1' });
405
- queue.clear();
406
-
407
- // 队列应该为空,锁也应该为空
408
- expect(queue.getStats().total).toBe(0);
409
- expect(queue.hasOrphanLocks()).toBe(false);
410
- });
411
- });
412
-
413
- describe('事件', () => {
414
- it('应该在创建时发出 announcement:created 事件', () => {
415
- const handler = vi.fn();
416
- queue.on('announcement:created', handler);
417
-
418
- queue.create({ taskType: 'test', description: 'Test', timeout: 5000, from: 'peer-1' });
419
-
420
- expect(handler).toHaveBeenCalledTimes(1);
421
- expect(handler.mock.calls[0][0].taskType).toBe('test');
422
- });
423
-
424
- it('应该在过期时发出 announcement:expired 事件', async () => {
425
- const handler = vi.fn();
426
- const fastExpireQueue = new AnnouncementQueue({ maxSize: 10, maxAgeMs: 50 });
427
- fastExpireQueue.on('announcement:expired', handler);
428
-
429
- fastExpireQueue.create({ taskType: 'test', description: 'Test', timeout: 5000, from: 'peer-1' });
430
-
431
- await new Promise(r => setTimeout(r, 60));
432
- fastExpireQueue.create({ taskType: 'test', description: 'New', timeout: 5000, from: 'peer-1' });
433
-
434
- expect(handler).toHaveBeenCalledTimes(1);
435
- expect(handler.mock.calls[0][0].reason).toBe('timeout');
436
- });
437
-
438
- it('应该在认领时发出 announcement:claimed 事件', () => {
439
- const handler = vi.fn();
440
- queue.on('announcement:claimed', handler);
441
-
442
- const announcement = queue.create({ taskType: 'test', description: 'Test', timeout: 5000, from: 'peer-1' });
443
- const claim = queue.submitClaim(announcement.announcementId, { claimant: 'peer-2' });
444
- queue.acceptClaim(announcement.announcementId, claim!.claimId);
445
-
446
- expect(handler).toHaveBeenCalledTimes(1);
447
- });
448
- });
449
- });