@f2a/network 0.1.2

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 (234) hide show
  1. package/.github/workflows/ci.yml +113 -0
  2. package/.github/workflows/publish.yml +60 -0
  3. package/LICENSE +21 -0
  4. package/MONOREPO.md +58 -0
  5. package/README.md +280 -0
  6. package/SKILL.md +137 -0
  7. package/dist/adapters/openclaw.d.ts +103 -0
  8. package/dist/adapters/openclaw.d.ts.map +1 -0
  9. package/dist/adapters/openclaw.js +297 -0
  10. package/dist/adapters/openclaw.js.map +1 -0
  11. package/dist/cli/commands.d.ts +17 -0
  12. package/dist/cli/commands.d.ts.map +1 -0
  13. package/dist/cli/commands.js +107 -0
  14. package/dist/cli/commands.js.map +1 -0
  15. package/dist/cli/index.d.ts +6 -0
  16. package/dist/cli/index.d.ts.map +1 -0
  17. package/dist/cli/index.js +203 -0
  18. package/dist/cli/index.js.map +1 -0
  19. package/dist/core/autonomous-economy.d.ts +136 -0
  20. package/dist/core/autonomous-economy.d.ts.map +1 -0
  21. package/dist/core/autonomous-economy.js +255 -0
  22. package/dist/core/autonomous-economy.js.map +1 -0
  23. package/dist/core/connection-manager.d.ts +80 -0
  24. package/dist/core/connection-manager.d.ts.map +1 -0
  25. package/dist/core/connection-manager.js +235 -0
  26. package/dist/core/connection-manager.js.map +1 -0
  27. package/dist/core/connection-manager.test.d.ts +2 -0
  28. package/dist/core/connection-manager.test.d.ts.map +1 -0
  29. package/dist/core/connection-manager.test.js +52 -0
  30. package/dist/core/connection-manager.test.js.map +1 -0
  31. package/dist/core/e2ee-crypto.d.ts +90 -0
  32. package/dist/core/e2ee-crypto.d.ts.map +1 -0
  33. package/dist/core/e2ee-crypto.js +190 -0
  34. package/dist/core/e2ee-crypto.js.map +1 -0
  35. package/dist/core/f2a.d.ts +126 -0
  36. package/dist/core/f2a.d.ts.map +1 -0
  37. package/dist/core/f2a.js +425 -0
  38. package/dist/core/f2a.js.map +1 -0
  39. package/dist/core/identity.d.ts +47 -0
  40. package/dist/core/identity.d.ts.map +1 -0
  41. package/dist/core/identity.js +130 -0
  42. package/dist/core/identity.js.map +1 -0
  43. package/dist/core/identity.test.d.ts +2 -0
  44. package/dist/core/identity.test.d.ts.map +1 -0
  45. package/dist/core/identity.test.js +43 -0
  46. package/dist/core/identity.test.js.map +1 -0
  47. package/dist/core/p2p-network.d.ts +242 -0
  48. package/dist/core/p2p-network.d.ts.map +1 -0
  49. package/dist/core/p2p-network.js +1182 -0
  50. package/dist/core/p2p-network.js.map +1 -0
  51. package/dist/core/reputation-security.d.ts +168 -0
  52. package/dist/core/reputation-security.d.ts.map +1 -0
  53. package/dist/core/reputation-security.js +369 -0
  54. package/dist/core/reputation-security.js.map +1 -0
  55. package/dist/core/reputation.d.ts +179 -0
  56. package/dist/core/reputation.d.ts.map +1 -0
  57. package/dist/core/reputation.js +472 -0
  58. package/dist/core/reputation.js.map +1 -0
  59. package/dist/core/review-committee.d.ts +130 -0
  60. package/dist/core/review-committee.d.ts.map +1 -0
  61. package/dist/core/review-committee.js +251 -0
  62. package/dist/core/review-committee.js.map +1 -0
  63. package/dist/core/serverless.d.ts +155 -0
  64. package/dist/core/serverless.d.ts.map +1 -0
  65. package/dist/core/serverless.js +615 -0
  66. package/dist/core/serverless.js.map +1 -0
  67. package/dist/core/token-manager.d.ts +42 -0
  68. package/dist/core/token-manager.d.ts.map +1 -0
  69. package/dist/core/token-manager.js +122 -0
  70. package/dist/core/token-manager.js.map +1 -0
  71. package/dist/daemon/control-server.d.ts +55 -0
  72. package/dist/daemon/control-server.d.ts.map +1 -0
  73. package/dist/daemon/control-server.js +262 -0
  74. package/dist/daemon/control-server.js.map +1 -0
  75. package/dist/daemon/index.d.ts +35 -0
  76. package/dist/daemon/index.d.ts.map +1 -0
  77. package/dist/daemon/index.js +69 -0
  78. package/dist/daemon/index.js.map +1 -0
  79. package/dist/daemon/main.d.ts +6 -0
  80. package/dist/daemon/main.d.ts.map +1 -0
  81. package/dist/daemon/main.js +38 -0
  82. package/dist/daemon/main.js.map +1 -0
  83. package/dist/daemon/start.d.ts +6 -0
  84. package/dist/daemon/start.d.ts.map +1 -0
  85. package/dist/daemon/start.js +25 -0
  86. package/dist/daemon/start.js.map +1 -0
  87. package/dist/daemon/webhook.d.ts +30 -0
  88. package/dist/daemon/webhook.d.ts.map +1 -0
  89. package/dist/daemon/webhook.js +86 -0
  90. package/dist/daemon/webhook.js.map +1 -0
  91. package/dist/daemon/webhook.test.d.ts +2 -0
  92. package/dist/daemon/webhook.test.d.ts.map +1 -0
  93. package/dist/daemon/webhook.test.js +24 -0
  94. package/dist/daemon/webhook.test.js.map +1 -0
  95. package/dist/index.d.ts +24 -0
  96. package/dist/index.d.ts.map +1 -0
  97. package/dist/index.js +25 -0
  98. package/dist/index.js.map +1 -0
  99. package/dist/protocol/messages.d.ts +739 -0
  100. package/dist/protocol/messages.d.ts.map +1 -0
  101. package/dist/protocol/messages.js +188 -0
  102. package/dist/protocol/messages.js.map +1 -0
  103. package/dist/protocol/messages.test.d.ts +2 -0
  104. package/dist/protocol/messages.test.d.ts.map +1 -0
  105. package/dist/protocol/messages.test.js +55 -0
  106. package/dist/protocol/messages.test.js.map +1 -0
  107. package/dist/types/index.d.ts +247 -0
  108. package/dist/types/index.d.ts.map +1 -0
  109. package/dist/types/index.js +10 -0
  110. package/dist/types/index.js.map +1 -0
  111. package/dist/types/result.d.ts +28 -0
  112. package/dist/types/result.d.ts.map +1 -0
  113. package/dist/types/result.js +16 -0
  114. package/dist/types/result.js.map +1 -0
  115. package/dist/utils/benchmark.d.ts +67 -0
  116. package/dist/utils/benchmark.d.ts.map +1 -0
  117. package/dist/utils/benchmark.js +179 -0
  118. package/dist/utils/benchmark.js.map +1 -0
  119. package/dist/utils/logger.d.ts +105 -0
  120. package/dist/utils/logger.d.ts.map +1 -0
  121. package/dist/utils/logger.js +275 -0
  122. package/dist/utils/logger.js.map +1 -0
  123. package/dist/utils/middleware.d.ts +85 -0
  124. package/dist/utils/middleware.d.ts.map +1 -0
  125. package/dist/utils/middleware.js +173 -0
  126. package/dist/utils/middleware.js.map +1 -0
  127. package/dist/utils/rate-limiter.d.ts +71 -0
  128. package/dist/utils/rate-limiter.d.ts.map +1 -0
  129. package/dist/utils/rate-limiter.js +160 -0
  130. package/dist/utils/rate-limiter.js.map +1 -0
  131. package/dist/utils/signature.d.ts +57 -0
  132. package/dist/utils/signature.d.ts.map +1 -0
  133. package/dist/utils/signature.js +102 -0
  134. package/dist/utils/signature.js.map +1 -0
  135. package/dist/utils/validation.d.ts +504 -0
  136. package/dist/utils/validation.d.ts.map +1 -0
  137. package/dist/utils/validation.js +159 -0
  138. package/dist/utils/validation.js.map +1 -0
  139. package/docs/F2A-PROTOCOL.md +61 -0
  140. package/docs/MOBILE_BOOTSTRAP_DESIGN.md +126 -0
  141. package/docs/a2a-lessons.md +316 -0
  142. package/docs/middleware-guide.md +448 -0
  143. package/docs/readme-update-checklist.md +90 -0
  144. package/docs/reputation-guide.md +396 -0
  145. package/docs/rfcs/001-reputation-system.md +712 -0
  146. package/docs/security-design.md +247 -0
  147. package/install.sh +231 -0
  148. package/package.json +64 -0
  149. package/packages/openclaw-adapter/README.md +510 -0
  150. package/packages/openclaw-adapter/openclaw.plugin.json +106 -0
  151. package/packages/openclaw-adapter/package.json +40 -0
  152. package/packages/openclaw-adapter/src/announcement-queue.test.ts +449 -0
  153. package/packages/openclaw-adapter/src/announcement-queue.ts +403 -0
  154. package/packages/openclaw-adapter/src/capability-detector.test.ts +99 -0
  155. package/packages/openclaw-adapter/src/capability-detector.ts +183 -0
  156. package/packages/openclaw-adapter/src/claim-handlers.test.ts +974 -0
  157. package/packages/openclaw-adapter/src/claim-handlers.ts +482 -0
  158. package/packages/openclaw-adapter/src/connector.business.test.ts +583 -0
  159. package/packages/openclaw-adapter/src/connector.ts +795 -0
  160. package/packages/openclaw-adapter/src/index.test.ts +82 -0
  161. package/packages/openclaw-adapter/src/index.ts +18 -0
  162. package/packages/openclaw-adapter/src/integration.e2e.test.ts +829 -0
  163. package/packages/openclaw-adapter/src/logger.ts +51 -0
  164. package/packages/openclaw-adapter/src/network-client.test.ts +266 -0
  165. package/packages/openclaw-adapter/src/network-client.ts +251 -0
  166. package/packages/openclaw-adapter/src/network-recovery.test.ts +465 -0
  167. package/packages/openclaw-adapter/src/node-manager.test.ts +136 -0
  168. package/packages/openclaw-adapter/src/node-manager.ts +429 -0
  169. package/packages/openclaw-adapter/src/plugin.test.ts +439 -0
  170. package/packages/openclaw-adapter/src/plugin.ts +104 -0
  171. package/packages/openclaw-adapter/src/reputation.test.ts +221 -0
  172. package/packages/openclaw-adapter/src/reputation.ts +368 -0
  173. package/packages/openclaw-adapter/src/task-guard.test.ts +502 -0
  174. package/packages/openclaw-adapter/src/task-guard.ts +860 -0
  175. package/packages/openclaw-adapter/src/task-queue.concurrency.test.ts +462 -0
  176. package/packages/openclaw-adapter/src/task-queue.edge-cases.test.ts +284 -0
  177. package/packages/openclaw-adapter/src/task-queue.persistence.test.ts +408 -0
  178. package/packages/openclaw-adapter/src/task-queue.ts +668 -0
  179. package/packages/openclaw-adapter/src/tool-handlers.test.ts +906 -0
  180. package/packages/openclaw-adapter/src/tool-handlers.ts +574 -0
  181. package/packages/openclaw-adapter/src/types.ts +361 -0
  182. package/packages/openclaw-adapter/src/webhook-pusher.test.ts +188 -0
  183. package/packages/openclaw-adapter/src/webhook-pusher.ts +220 -0
  184. package/packages/openclaw-adapter/src/webhook-server.test.ts +580 -0
  185. package/packages/openclaw-adapter/src/webhook-server.ts +202 -0
  186. package/packages/openclaw-adapter/tsconfig.json +20 -0
  187. package/src/cli/commands.test.ts +157 -0
  188. package/src/cli/commands.ts +129 -0
  189. package/src/cli/index.test.ts +77 -0
  190. package/src/cli/index.ts +234 -0
  191. package/src/core/autonomous-economy.test.ts +291 -0
  192. package/src/core/autonomous-economy.ts +428 -0
  193. package/src/core/e2ee-crypto.test.ts +125 -0
  194. package/src/core/e2ee-crypto.ts +246 -0
  195. package/src/core/f2a.test.ts +269 -0
  196. package/src/core/f2a.ts +618 -0
  197. package/src/core/p2p-network.test.ts +199 -0
  198. package/src/core/p2p-network.ts +1432 -0
  199. package/src/core/reputation-security.test.ts +403 -0
  200. package/src/core/reputation-security.ts +562 -0
  201. package/src/core/reputation.test.ts +260 -0
  202. package/src/core/reputation.ts +576 -0
  203. package/src/core/review-committee.test.ts +380 -0
  204. package/src/core/review-committee.ts +401 -0
  205. package/src/core/token-manager.test.ts +133 -0
  206. package/src/core/token-manager.ts +140 -0
  207. package/src/daemon/control-server.test.ts +216 -0
  208. package/src/daemon/control-server.ts +292 -0
  209. package/src/daemon/index.test.ts +85 -0
  210. package/src/daemon/index.ts +89 -0
  211. package/src/daemon/main.ts +44 -0
  212. package/src/daemon/start.ts +29 -0
  213. package/src/daemon/webhook.test.ts +68 -0
  214. package/src/daemon/webhook.ts +105 -0
  215. package/src/index.test.ts +436 -0
  216. package/src/index.ts +72 -0
  217. package/src/types/index.test.ts +87 -0
  218. package/src/types/index.ts +341 -0
  219. package/src/types/result.ts +68 -0
  220. package/src/utils/benchmark.ts +237 -0
  221. package/src/utils/logger.ts +331 -0
  222. package/src/utils/middleware.ts +229 -0
  223. package/src/utils/rate-limiter.ts +207 -0
  224. package/src/utils/signature.ts +136 -0
  225. package/src/utils/validation.ts +186 -0
  226. package/tests/docker/Dockerfile.node +23 -0
  227. package/tests/docker/Dockerfile.runner +18 -0
  228. package/tests/docker/docker-compose.test.yml +73 -0
  229. package/tests/integration/message-passing.test.ts +109 -0
  230. package/tests/integration/multi-node.test.ts +92 -0
  231. package/tests/integration/p2p-connection.test.ts +83 -0
  232. package/tests/integration/test-config.ts +32 -0
  233. package/tsconfig.json +21 -0
  234. package/vitest.config.ts +26 -0
@@ -0,0 +1,462 @@
1
+ /**
2
+ * TaskQueue 真实并发测试
3
+ * 使用 worker_threads 测试并发访问
4
+ */
5
+
6
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
7
+ import { TaskQueue } from './task-queue.js';
8
+ import { Worker, isMainThread, parentPort, workerData } from 'worker_threads';
9
+ import path from 'path';
10
+ import fs from 'fs';
11
+ import { fileURLToPath } from 'url';
12
+
13
+ const __filename = fileURLToPath(import.meta.url);
14
+ const __dirname = path.dirname(__filename);
15
+
16
+ const TEST_DIR = './test-tmp-concurrency';
17
+
18
+ // Worker 脚本内容
19
+ const workerScript = `
20
+ import { parentPort, workerData } from 'worker_threads';
21
+ import { TaskQueue } from './task-queue.js';
22
+
23
+ const { action, taskId, persistDir } = workerData;
24
+
25
+ const queue = new TaskQueue({
26
+ maxSize: 1000,
27
+ persistDir: persistDir,
28
+ persistEnabled: true
29
+ });
30
+
31
+ try {
32
+ let result;
33
+ switch (action) {
34
+ case 'add':
35
+ result = queue.add({ taskId, taskType: 'concurrent-test', description: 'Test task' });
36
+ break;
37
+ case 'getPending':
38
+ result = queue.getPending(10);
39
+ break;
40
+ case 'markProcessing':
41
+ result = queue.markProcessing(taskId);
42
+ break;
43
+ case 'complete':
44
+ result = queue.complete(taskId, { status: 'success', result: 'done' });
45
+ break;
46
+ case 'getStats':
47
+ result = queue.getStats();
48
+ break;
49
+ default:
50
+ throw new Error('Unknown action: ' + action);
51
+ }
52
+ parentPort.postMessage({ success: true, result });
53
+ } catch (error) {
54
+ parentPort.postMessage({ success: false, error: error.message });
55
+ } finally {
56
+ queue.close();
57
+ }
58
+ `;
59
+
60
+ describe('TaskQueue 真实并发测试', () => {
61
+ let queue: TaskQueue;
62
+
63
+ beforeEach(() => {
64
+ if (!fs.existsSync(TEST_DIR)) {
65
+ fs.mkdirSync(TEST_DIR, { recursive: true });
66
+ }
67
+ queue = new TaskQueue({
68
+ maxSize: 1000,
69
+ persistDir: TEST_DIR,
70
+ persistEnabled: true
71
+ });
72
+ });
73
+
74
+ afterEach(() => {
75
+ queue.close();
76
+ if (fs.existsSync(TEST_DIR)) {
77
+ fs.rmSync(TEST_DIR, { recursive: true });
78
+ }
79
+ });
80
+
81
+ describe('使用 Promise.all 测试并发', () => {
82
+ it('应该正确处理并发添加 100 个任务', async () => {
83
+ const promises = [];
84
+
85
+ for (let i = 0; i < 100; i++) {
86
+ promises.push(
87
+ new Promise<void>((resolve) => {
88
+ // 模拟异步操作
89
+ setImmediate(() => {
90
+ queue.add({ taskId: `task-${i}`, taskType: 'concurrent' });
91
+ resolve();
92
+ });
93
+ })
94
+ );
95
+ }
96
+
97
+ await Promise.all(promises);
98
+
99
+ const stats = queue.getStats();
100
+ expect(stats.total).toBe(100);
101
+ expect(stats.pending).toBe(100);
102
+ });
103
+
104
+ it('应该正确处理并发 add 和 getPending', async () => {
105
+ const addPromises: Promise<void>[] = [];
106
+ const getPromises: Promise<void>[] = [];
107
+
108
+ // 同时添加和获取
109
+ for (let i = 0; i < 50; i++) {
110
+ addPromises.push(
111
+ new Promise<void>((resolve) => {
112
+ setImmediate(() => {
113
+ queue.add({ taskId: `task-${i}`, taskType: 'race-test' });
114
+ resolve();
115
+ });
116
+ })
117
+ );
118
+
119
+ getPromises.push(
120
+ new Promise<void>((resolve) => {
121
+ setImmediate(() => {
122
+ queue.getPending();
123
+ resolve();
124
+ });
125
+ })
126
+ );
127
+ }
128
+
129
+ await Promise.all([...addPromises, ...getPromises]);
130
+
131
+ const stats = queue.getStats();
132
+ expect(stats.total).toBe(50);
133
+ });
134
+
135
+ it('应该正确处理并发 markProcessing', async () => {
136
+ // 先添加任务
137
+ for (let i = 0; i < 20; i++) {
138
+ queue.add({ taskId: `task-${i}`, taskType: 'processing-test' });
139
+ }
140
+
141
+ const promises = [];
142
+ for (let i = 0; i < 20; i++) {
143
+ promises.push(
144
+ new Promise<void>((resolve) => {
145
+ setImmediate(() => {
146
+ queue.markProcessing(`task-${i}`);
147
+ resolve();
148
+ });
149
+ })
150
+ );
151
+ }
152
+
153
+ await Promise.all(promises);
154
+
155
+ const stats = queue.getStats();
156
+ expect(stats.processing).toBe(20);
157
+ });
158
+
159
+ it('应该正确处理并发 complete', async () => {
160
+ // 先添加任务
161
+ for (let i = 0; i < 30; i++) {
162
+ queue.add({ taskId: `task-${i}`, taskType: 'complete-test' });
163
+ }
164
+
165
+ const promises = [];
166
+ for (let i = 0; i < 30; i++) {
167
+ promises.push(
168
+ new Promise<void>((resolve) => {
169
+ setImmediate(() => {
170
+ queue.complete(`task-${i}`, { status: 'success', result: `result-${i}` });
171
+ resolve();
172
+ });
173
+ })
174
+ );
175
+ }
176
+
177
+ await Promise.all(promises);
178
+
179
+ const stats = queue.getStats();
180
+ expect(stats.completed).toBe(30);
181
+ });
182
+ });
183
+
184
+ describe('竞态条件测试', () => {
185
+ it('应该正确处理 add 和 delete 的竞态', async () => {
186
+ // 先添加一些任务
187
+ for (let i = 0; i < 50; i++) {
188
+ queue.add({ taskId: `task-${i}`, taskType: 'race-delete' });
189
+ }
190
+
191
+ const addPromises: Promise<void>[] = [];
192
+ const deletePromises: Promise<void>[] = [];
193
+
194
+ // 添加新任务
195
+ for (let i = 50; i < 100; i++) {
196
+ addPromises.push(
197
+ new Promise<void>((resolve) => {
198
+ setImmediate(() => {
199
+ try {
200
+ queue.add({ taskId: `task-${i}`, taskType: 'race-delete' });
201
+ } catch (e) {
202
+ // 忽略错误
203
+ }
204
+ resolve();
205
+ });
206
+ })
207
+ );
208
+ }
209
+
210
+ // 同时删除旧任务
211
+ for (let i = 0; i < 50; i++) {
212
+ deletePromises.push(
213
+ new Promise<void>((resolve) => {
214
+ setImmediate(() => {
215
+ queue.delete(`task-${i}`);
216
+ resolve();
217
+ });
218
+ })
219
+ );
220
+ }
221
+
222
+ await Promise.all([...addPromises, ...deletePromises]);
223
+
224
+ // 最终队列应该有 50 个任务(新添加的)
225
+ const stats = queue.getStats();
226
+ expect(stats.total).toBe(50);
227
+ });
228
+
229
+ it('应该正确处理同一任务的并发操作', async () => {
230
+ queue.add({ taskId: 'concurrent-task', taskType: 'same-task' });
231
+
232
+ const promises = [
233
+ // 同时进行多种操作
234
+ new Promise<void>((resolve) => {
235
+ setImmediate(() => {
236
+ queue.markProcessing('concurrent-task');
237
+ resolve();
238
+ });
239
+ }),
240
+ new Promise<void>((resolve) => {
241
+ setImmediate(() => {
242
+ queue.complete('concurrent-task', { status: 'success', result: 'done' });
243
+ resolve();
244
+ });
245
+ }),
246
+ new Promise<void>((resolve) => {
247
+ setImmediate(() => {
248
+ queue.get('concurrent-task');
249
+ resolve();
250
+ });
251
+ }),
252
+ ];
253
+
254
+ await Promise.all(promises);
255
+
256
+ // 任务最终应该处于某个一致状态
257
+ const task = queue.get('concurrent-task');
258
+ expect(task).toBeDefined();
259
+ expect(['processing', 'completed']).toContain(task?.status);
260
+ });
261
+ });
262
+
263
+ describe('使用 setImmediate 模拟时间片', () => {
264
+ it('应该在高并发下保持数据一致性', async () => {
265
+ const operations: Promise<void>[] = [];
266
+
267
+ // 创建大量并发操作
268
+ for (let round = 0; round < 5; round++) {
269
+ for (let i = 0; i < 20; i++) {
270
+ const taskId = `task-${round}-${i}`;
271
+ operations.push(
272
+ new Promise<void>((resolve) => {
273
+ setImmediate(() => {
274
+ queue.add({ taskId, taskType: 'high-concurrency' });
275
+ resolve();
276
+ });
277
+ })
278
+ );
279
+ }
280
+ }
281
+
282
+ await Promise.all(operations);
283
+
284
+ // 验证数据一致性
285
+ const stats = queue.getStats();
286
+ expect(stats.total).toBe(100);
287
+
288
+ // 验证每个任务都能被正确获取
289
+ for (let round = 0; round < 5; round++) {
290
+ for (let i = 0; i < 20; i++) {
291
+ const taskId = `task-${round}-${i}`;
292
+ const task = queue.get(taskId);
293
+ expect(task).toBeDefined();
294
+ expect(task?.status).toBe('pending');
295
+ }
296
+ }
297
+ });
298
+
299
+ it('应该在并发清理下保持稳定', async () => {
300
+ const shortLivedQueue = new TaskQueue({
301
+ maxSize: 1000,
302
+ maxAgeMs: 100, // 100ms 过期
303
+ persistDir: TEST_DIR,
304
+ persistEnabled: true
305
+ });
306
+
307
+ // 添加任务
308
+ for (let i = 0; i < 50; i++) {
309
+ shortLivedQueue.add({ taskId: `expiring-${i}`, taskType: 'cleanup-test' });
310
+ }
311
+
312
+ // 等待任务过期
313
+ await new Promise(r => setTimeout(r, 150));
314
+
315
+ // 并发触发清理(通过添加新任务)
316
+ const promises: Promise<void>[] = [];
317
+ for (let i = 0; i < 20; i++) {
318
+ promises.push(
319
+ new Promise<void>((resolve) => {
320
+ setImmediate(() => {
321
+ shortLivedQueue.add({ taskId: `new-${i}`, taskType: 'cleanup-test' });
322
+ resolve();
323
+ });
324
+ })
325
+ );
326
+ }
327
+
328
+ await Promise.all(promises);
329
+
330
+ const stats = shortLivedQueue.getStats();
331
+ // 新任务应该存在,旧任务应该被清理
332
+ expect(stats.total).toBe(20);
333
+
334
+ shortLivedQueue.close();
335
+ });
336
+ });
337
+
338
+ describe('事务原子性测试', () => {
339
+ it('应该在事务失败时回滚', async () => {
340
+ // 填满队列
341
+ for (let i = 0; i < 1000; i++) {
342
+ queue.add({ taskId: `fill-${i}`, taskType: 'fill' });
343
+ }
344
+
345
+ const failedAdds: Promise<void>[] = [];
346
+ const successfulAdds: Promise<void>[] = [];
347
+
348
+ // 尝试在满队列上添加任务(应该失败)
349
+ for (let i = 0; i < 10; i++) {
350
+ failedAdds.push(
351
+ new Promise<void>((resolve) => {
352
+ setImmediate(() => {
353
+ try {
354
+ queue.add({ taskId: `overflow-${i}`, taskType: 'overflow' });
355
+ } catch (e) {
356
+ // 预期会失败
357
+ }
358
+ resolve();
359
+ });
360
+ })
361
+ );
362
+ }
363
+
364
+ await Promise.all(failedAdds);
365
+
366
+ // 队列大小应该保持 1000
367
+ const stats = queue.getStats();
368
+ expect(stats.total).toBe(1000);
369
+ });
370
+
371
+ it('应该在并发删除和查询时保持一致性', async () => {
372
+ for (let i = 0; i < 50; i++) {
373
+ queue.add({ taskId: `delete-test-${i}`, taskType: 'delete' });
374
+ }
375
+
376
+ const deletePromises: Promise<void>[] = [];
377
+ const getPromises: Promise<void>[] = [];
378
+
379
+ for (let i = 0; i < 50; i++) {
380
+ const taskId = `delete-test-${i}`;
381
+
382
+ deletePromises.push(
383
+ new Promise<void>((resolve) => {
384
+ setImmediate(() => {
385
+ queue.delete(taskId);
386
+ resolve();
387
+ });
388
+ })
389
+ );
390
+
391
+ getPromises.push(
392
+ new Promise<void>((resolve) => {
393
+ setImmediate(() => {
394
+ queue.get(taskId);
395
+ resolve();
396
+ });
397
+ })
398
+ );
399
+ }
400
+
401
+ await Promise.all([...deletePromises, ...getPromises]);
402
+
403
+ // 所有任务应该被删除
404
+ const stats = queue.getStats();
405
+ expect(stats.total).toBe(0);
406
+ });
407
+ });
408
+
409
+ describe('Webhook 相关并发测试', () => {
410
+ it('应该正确处理并发 markWebhookPushed', async () => {
411
+ for (let i = 0; i < 20; i++) {
412
+ queue.add({ taskId: `webhook-${i}`, taskType: 'webhook' });
413
+ }
414
+
415
+ const promises: Promise<void>[] = [];
416
+ for (let i = 0; i < 20; i++) {
417
+ promises.push(
418
+ new Promise<void>((resolve) => {
419
+ setImmediate(() => {
420
+ queue.markWebhookPushed(`webhook-${i}`);
421
+ resolve();
422
+ });
423
+ })
424
+ );
425
+ }
426
+
427
+ await Promise.all(promises);
428
+
429
+ const webhookPending = queue.getWebhookPending();
430
+ expect(webhookPending.length).toBe(0);
431
+ });
432
+
433
+ it('应该在并发 getWebhookPending 时保持一致性', async () => {
434
+ for (let i = 0; i < 30; i++) {
435
+ queue.add({ taskId: `concurrent-webhook-${i}`, taskType: 'webhook' });
436
+ }
437
+
438
+ const promises: Promise<unknown>[] = [];
439
+
440
+ // 并发获取和标记
441
+ for (let i = 0; i < 30; i++) {
442
+ promises.push(
443
+ new Promise((resolve) => {
444
+ setImmediate(() => {
445
+ const pending = queue.getWebhookPending();
446
+ if (pending.length > 0) {
447
+ queue.markWebhookPushed(pending[0].taskId);
448
+ }
449
+ resolve(pending.length);
450
+ });
451
+ })
452
+ );
453
+ }
454
+
455
+ const results = await Promise.all(promises);
456
+
457
+ // 最终所有任务都应该被标记
458
+ const finalPending = queue.getWebhookPending();
459
+ expect(finalPending.length).toBe(0);
460
+ });
461
+ });
462
+ });