claw-dashboard 2.1.1 → 2.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/CHANGELOG.md +29 -1
- package/docs/API.md +1 -2
- package/index.js +31 -41
- package/package.json +22 -12
- package/src/auto-save.js +11 -5
- package/src/cli/export-snapshot.js +3 -1
- package/src/cli/import-snapshot.js +3 -1
- package/src/config.js +9 -15
- package/src/errors.js +0 -9
- package/src/security.js +6 -2
- package/src/snapshot.js +73 -26
- package/src/web-server.js +7 -10
- package/.c8rc.json +0 -38
- package/.dockerignore +0 -68
- package/.github/dependabot.yml +0 -45
- package/.github/pull_request_template.md +0 -39
- package/.github/workflows/ci.yml +0 -147
- package/.github/workflows/release.yml +0 -40
- package/.github/workflows/security.yml +0 -84
- package/.husky/pre-commit +0 -1
- package/.planning/codebase/ARCHITECTURE.md +0 -206
- package/.planning/codebase/INTEGRATIONS.md +0 -150
- package/.planning/codebase/STACK.md +0 -122
- package/.planning/codebase/STRUCTURE.md +0 -201
- package/CONTRIBUTING.md +0 -378
- package/Dockerfile +0 -54
- package/FEATURES.md +0 -307
- package/TODO.md +0 -28
- package/ai.openclaw.dashboard.plist +0 -35
- package/build-cjs.js +0 -127
- package/cjs-shim.js +0 -13
- package/dist/clawdash +0 -1729
- package/dist/clawdash.meta.json +0 -2236
- package/dist/widgets.cjs +0 -6511
- package/docker-compose.yml +0 -67
- package/esbuild.config.js +0 -158
- package/eslint.config.js +0 -56
- package/examples/plugins/README.md +0 -122
- package/examples/plugins/api-status/index.js +0 -294
- package/examples/plugins/api-status/plugin.json +0 -19
- package/examples/plugins/hello-world/index.js +0 -104
- package/examples/plugins/hello-world/plugin.json +0 -15
- package/examples/plugins/system-metrics-chart/index.js +0 -339
- package/examples/plugins/system-metrics-chart/plugin.json +0 -18
- package/examples/plugins/weather-widget/index.js +0 -136
- package/examples/plugins/weather-widget/plugin.json +0 -19
- package/index.cjs +0 -23005
- package/jest.config.js +0 -11
- package/scripts/release.js +0 -595
- package/src/database.js +0 -734
- package/tests/alerts.test.js +0 -437
- package/tests/auto-save.test.js +0 -529
- package/tests/cache.test.js +0 -317
- package/tests/cli.test.js +0 -351
- package/tests/command-palette.test.js +0 -320
- package/tests/config-processor.test.js +0 -452
- package/tests/config-validator.test.js +0 -710
- package/tests/config-watcher.test.js +0 -594
- package/tests/config.test.js +0 -755
- package/tests/database.test.js +0 -438
- package/tests/errors.test.js +0 -189
- package/tests/example-plugins.test.js +0 -624
- package/tests/integration.test.js +0 -1321
- package/tests/loading-states.test.js +0 -300
- package/tests/manifest-validation-on-load.test.js +0 -671
- package/tests/performance-monitor.test.js +0 -190
- package/tests/plugin-api-rate-limit.test.js +0 -302
- package/tests/plugin-errors.test.js +0 -311
- package/tests/plugin-lifecycle-e2e.test.js +0 -1036
- package/tests/plugin-reload.test.js +0 -764
- package/tests/rate-limiter.test.js +0 -539
- package/tests/removed-dependencies.test.js +0 -74
- package/tests/retry.test.js +0 -308
- package/tests/security.test.js +0 -411
- package/tests/settings-modal.test.js +0 -226
- package/tests/theme-selector.test.js +0 -57
- package/tests/utils.js +0 -242
- package/tests/utils.test.js +0 -317
- package/tests/validate-plugin-cli.test.js +0 -359
- package/tests/validation.test.js +0 -837
- package/tests/web-server.test.js +0 -646
- package/tests/widget-arrange-mode.test.js +0 -183
- package/tests/widget-config-hot-reload.test.js +0 -465
- package/tests/widget-dependency.test.js +0 -591
- package/tests/widget-error-boundary.test.js +0 -749
- package/tests/widget-error-isolation.test.js +0 -469
- package/tests/widget-integration.test.js +0 -1147
- package/tests/widget-refresh-intervals.test.js +0 -284
- package/tests/worker-pool.test.js +0 -522
|
@@ -1,522 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for WorkerPool - task execution, timeout handling, worker recovery, error propagation
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { jest } from '@jest/globals';
|
|
6
|
-
import { WorkerPool } from '../src/workers/worker-pool.js';
|
|
7
|
-
|
|
8
|
-
// Mock systeminformation for fallback tests
|
|
9
|
-
const mockSiData = {
|
|
10
|
-
currentLoad: { avgLoad: 1.5, currentLoad: 25 },
|
|
11
|
-
mem: { total: 16000000000, used: 8000000000 },
|
|
12
|
-
graphics: { controllers: [] },
|
|
13
|
-
networkStats: [{ iface: 'eth0', rx_bytes: 1000 }],
|
|
14
|
-
fsSize: [{ fs: '/', size: 1000000000 }],
|
|
15
|
-
osInfo: { platform: 'darwin', hostname: 'test-host' },
|
|
16
|
-
versions: { node: '18.0.0' },
|
|
17
|
-
time: { uptime: 3600 },
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
jest.unstable_mockModule('systeminformation', () => ({
|
|
21
|
-
default: {
|
|
22
|
-
currentLoad: jest.fn().mockResolvedValue(mockSiData.currentLoad),
|
|
23
|
-
mem: jest.fn().mockResolvedValue(mockSiData.mem),
|
|
24
|
-
graphics: jest.fn().mockResolvedValue(mockSiData.graphics),
|
|
25
|
-
networkStats: jest.fn().mockResolvedValue(mockSiData.networkStats),
|
|
26
|
-
fsSize: jest.fn().mockResolvedValue(mockSiData.fsSize),
|
|
27
|
-
osInfo: jest.fn().mockResolvedValue(mockSiData.osInfo),
|
|
28
|
-
versions: jest.fn().mockResolvedValue(mockSiData.versions),
|
|
29
|
-
time: jest.fn().mockResolvedValue(mockSiData.time),
|
|
30
|
-
},
|
|
31
|
-
}));
|
|
32
|
-
|
|
33
|
-
// Mock logger
|
|
34
|
-
jest.unstable_mockModule('../src/logger.js', () => ({
|
|
35
|
-
default: {
|
|
36
|
-
info: jest.fn(),
|
|
37
|
-
warn: jest.fn(),
|
|
38
|
-
error: jest.fn(),
|
|
39
|
-
debug: jest.fn(),
|
|
40
|
-
},
|
|
41
|
-
}));
|
|
42
|
-
|
|
43
|
-
describe('WorkerPool', () => {
|
|
44
|
-
let pool;
|
|
45
|
-
|
|
46
|
-
afterEach(async () => {
|
|
47
|
-
if (pool) {
|
|
48
|
-
await pool.shutdown();
|
|
49
|
-
pool = null;
|
|
50
|
-
}
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
describe('constructor', () => {
|
|
54
|
-
test('should create WorkerPool with default options from config', async () => {
|
|
55
|
-
pool = new WorkerPool();
|
|
56
|
-
|
|
57
|
-
// Uses config.js defaults since we can't mock it effectively in ES modules
|
|
58
|
-
expect(pool.taskTimeout).toBe(10000); // Default from worker-pool.js
|
|
59
|
-
expect(pool.enableWorkers).toBe(true);
|
|
60
|
-
expect(pool.workersSupported).toBe(true);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
test('should create WorkerPool with custom options', async () => {
|
|
64
|
-
pool = new WorkerPool({
|
|
65
|
-
maxWorkers: 4,
|
|
66
|
-
taskTimeout: 5000,
|
|
67
|
-
enableWorkers: false,
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
expect(pool.maxWorkers).toBe(4);
|
|
71
|
-
expect(pool.taskTimeout).toBe(5000);
|
|
72
|
-
expect(pool.enableWorkers).toBe(false);
|
|
73
|
-
expect(pool.workers.length).toBe(0);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
test('should respect workersSupported flag when disabled', async () => {
|
|
77
|
-
pool = new WorkerPool({ enableWorkers: false });
|
|
78
|
-
expect(pool.workersSupported).toBe(true);
|
|
79
|
-
expect(pool.workers.length).toBe(0);
|
|
80
|
-
});
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
describe('task execution', () => {
|
|
84
|
-
test('should execute task and return result via fallback', async () => {
|
|
85
|
-
pool = new WorkerPool({ enableWorkers: false });
|
|
86
|
-
|
|
87
|
-
const result = await pool.execute('currentLoad');
|
|
88
|
-
|
|
89
|
-
expect(result).toEqual(mockSiData.currentLoad);
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
test('should execute multiple commands', async () => {
|
|
93
|
-
pool = new WorkerPool({ enableWorkers: false });
|
|
94
|
-
|
|
95
|
-
const result1 = await pool.execute('mem');
|
|
96
|
-
const result2 = await pool.execute('fsSize');
|
|
97
|
-
|
|
98
|
-
expect(result1).toEqual(mockSiData.mem);
|
|
99
|
-
expect(result2).toEqual(mockSiData.fsSize);
|
|
100
|
-
});
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
describe('fallback execution', () => {
|
|
104
|
-
test('should use fallback when workers are disabled', async () => {
|
|
105
|
-
pool = new WorkerPool({ enableWorkers: false });
|
|
106
|
-
|
|
107
|
-
const result = await pool.execute('currentLoad');
|
|
108
|
-
|
|
109
|
-
expect(result).toEqual(mockSiData.currentLoad);
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
test('should use fallback when workers are not supported', async () => {
|
|
113
|
-
pool = new WorkerPool({ enableWorkers: false });
|
|
114
|
-
pool.workersSupported = false;
|
|
115
|
-
|
|
116
|
-
const result = await pool.execute('mem');
|
|
117
|
-
|
|
118
|
-
expect(result).toEqual(mockSiData.mem);
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
test('should fallback execute currentLoad command', async () => {
|
|
122
|
-
pool = new WorkerPool({ enableWorkers: false });
|
|
123
|
-
|
|
124
|
-
const result = await pool.execute('currentLoad');
|
|
125
|
-
expect(result).toEqual(mockSiData.currentLoad);
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
test('should fallback execute mem command', async () => {
|
|
129
|
-
pool = new WorkerPool({ enableWorkers: false });
|
|
130
|
-
|
|
131
|
-
const result = await pool.execute('mem');
|
|
132
|
-
expect(result).toEqual(mockSiData.mem);
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
test('should fallback execute graphics command', async () => {
|
|
136
|
-
pool = new WorkerPool({ enableWorkers: false });
|
|
137
|
-
|
|
138
|
-
const result = await pool.execute('graphics');
|
|
139
|
-
expect(result).toEqual(mockSiData.graphics);
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
test('should fallback execute networkStats command', async () => {
|
|
143
|
-
pool = new WorkerPool({ enableWorkers: false });
|
|
144
|
-
|
|
145
|
-
const result = await pool.execute('networkStats');
|
|
146
|
-
expect(result).toEqual(mockSiData.networkStats);
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
test('should fallback execute fsSize command', async () => {
|
|
150
|
-
pool = new WorkerPool({ enableWorkers: false });
|
|
151
|
-
|
|
152
|
-
const result = await pool.execute('fsSize');
|
|
153
|
-
expect(result).toEqual(mockSiData.fsSize);
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
test('should fallback execute systemData command', async () => {
|
|
157
|
-
pool = new WorkerPool({ enableWorkers: false });
|
|
158
|
-
|
|
159
|
-
const result = await pool.execute('systemData');
|
|
160
|
-
|
|
161
|
-
expect(result).toEqual({
|
|
162
|
-
os: mockSiData.osInfo,
|
|
163
|
-
ver: mockSiData.versions,
|
|
164
|
-
time: mockSiData.time,
|
|
165
|
-
});
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
test('should throw for unknown command in fallback', async () => {
|
|
169
|
-
pool = new WorkerPool({ enableWorkers: false });
|
|
170
|
-
|
|
171
|
-
await expect(pool.execute('unknownCommand')).rejects.toThrow('Unknown command: unknownCommand');
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
test('should propagate fallback execution errors', async () => {
|
|
175
|
-
const { default: systemInfo } = await import('systeminformation');
|
|
176
|
-
pool = new WorkerPool({ enableWorkers: false });
|
|
177
|
-
|
|
178
|
-
systemInfo.currentLoad.mockRejectedValueOnce(new Error('SI Error'));
|
|
179
|
-
|
|
180
|
-
await expect(pool.execute('currentLoad')).rejects.toThrow('SI Error');
|
|
181
|
-
});
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
describe('getStatus', () => {
|
|
185
|
-
test('should return pool status with workers disabled', async () => {
|
|
186
|
-
pool = new WorkerPool({ enableWorkers: false });
|
|
187
|
-
|
|
188
|
-
const status = pool.getStatus();
|
|
189
|
-
|
|
190
|
-
expect(status.enabled).toBe(false);
|
|
191
|
-
expect(status.supported).toBe(true);
|
|
192
|
-
expect(status.totalWorkers).toBe(0);
|
|
193
|
-
expect(status.busyWorkers).toBe(0);
|
|
194
|
-
expect(status.readyWorkers).toBe(0);
|
|
195
|
-
expect(status.pendingTasks).toBe(0);
|
|
196
|
-
expect(status.queuedTasks).toBe(0);
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
test('should count pending and queued tasks correctly', async () => {
|
|
200
|
-
pool = new WorkerPool({ enableWorkers: false });
|
|
201
|
-
|
|
202
|
-
// Manually add some pending and queued tasks
|
|
203
|
-
pool.pendingTasks.set(1, {
|
|
204
|
-
id: 1,
|
|
205
|
-
command: 'test',
|
|
206
|
-
resolve: () => {},
|
|
207
|
-
reject: () => {},
|
|
208
|
-
timeout: null,
|
|
209
|
-
});
|
|
210
|
-
pool.taskQueue.push({ id: 2, command: 'test2' });
|
|
211
|
-
|
|
212
|
-
const status = pool.getStatus();
|
|
213
|
-
expect(status.pendingTasks).toBe(1);
|
|
214
|
-
expect(status.queuedTasks).toBe(1);
|
|
215
|
-
});
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
describe('shutdown', () => {
|
|
219
|
-
test('should set shutdown flag', async () => {
|
|
220
|
-
pool = new WorkerPool({ enableWorkers: false });
|
|
221
|
-
|
|
222
|
-
expect(pool.isShutdown).toBe(false);
|
|
223
|
-
await pool.shutdown();
|
|
224
|
-
|
|
225
|
-
expect(pool.isShutdown).toBe(true);
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
test('should reject pending tasks on shutdown', async () => {
|
|
229
|
-
pool = new WorkerPool({ enableWorkers: false, taskTimeout: 10000 });
|
|
230
|
-
|
|
231
|
-
// Add a pending task with proper reject function
|
|
232
|
-
const rejectFn = jest.fn();
|
|
233
|
-
pool.pendingTasks.set(1, {
|
|
234
|
-
id: 1,
|
|
235
|
-
command: 'test',
|
|
236
|
-
resolve: () => {},
|
|
237
|
-
reject: rejectFn,
|
|
238
|
-
timeout: null,
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
await pool.shutdown();
|
|
242
|
-
|
|
243
|
-
expect(rejectFn).toHaveBeenCalledWith(new Error('Worker pool shutting down'));
|
|
244
|
-
expect(pool.pendingTasks.size).toBe(0);
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
test('should clear task queue on shutdown', async () => {
|
|
248
|
-
pool = new WorkerPool({ enableWorkers: false });
|
|
249
|
-
|
|
250
|
-
// Add tasks to queue
|
|
251
|
-
pool.taskQueue.push({ id: 1, command: 'test' });
|
|
252
|
-
pool.taskQueue.push({ id: 2, command: 'test2' });
|
|
253
|
-
|
|
254
|
-
await pool.shutdown();
|
|
255
|
-
|
|
256
|
-
expect(pool.taskQueue.length).toBe(0);
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
test('should be safe to call shutdown multiple times', async () => {
|
|
260
|
-
pool = new WorkerPool({ enableWorkers: false });
|
|
261
|
-
|
|
262
|
-
await pool.shutdown();
|
|
263
|
-
await pool.shutdown(); // Should not throw
|
|
264
|
-
|
|
265
|
-
expect(pool.isShutdown).toBe(true);
|
|
266
|
-
});
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
describe('checkWorkerSupport', () => {
|
|
270
|
-
test('should return true for Node.js 12+', () => {
|
|
271
|
-
pool = new WorkerPool();
|
|
272
|
-
expect(pool.checkWorkerSupport()).toBe(true);
|
|
273
|
-
});
|
|
274
|
-
});
|
|
275
|
-
|
|
276
|
-
describe('task ID generation', () => {
|
|
277
|
-
test('taskId starts at 0', () => {
|
|
278
|
-
pool = new WorkerPool({ enableWorkers: false });
|
|
279
|
-
expect(pool.taskId).toBe(0);
|
|
280
|
-
});
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
describe('processQueue edge cases', () => {
|
|
284
|
-
test('should handle empty queue', async () => {
|
|
285
|
-
pool = new WorkerPool({ enableWorkers: false });
|
|
286
|
-
|
|
287
|
-
// Should not throw
|
|
288
|
-
pool.processQueue();
|
|
289
|
-
expect(pool.taskQueue.length).toBe(0);
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
test('should not process queue when workers are disabled', async () => {
|
|
293
|
-
pool = new WorkerPool({ enableWorkers: false });
|
|
294
|
-
|
|
295
|
-
// Add task to queue
|
|
296
|
-
pool.taskQueue.push({ id: 1, command: 'test', options: {} });
|
|
297
|
-
|
|
298
|
-
// Try to process
|
|
299
|
-
pool.processQueue();
|
|
300
|
-
|
|
301
|
-
// Task should remain in queue since no workers
|
|
302
|
-
expect(pool.taskQueue.length).toBe(1);
|
|
303
|
-
});
|
|
304
|
-
});
|
|
305
|
-
|
|
306
|
-
describe('worker management', () => {
|
|
307
|
-
test('should create and remove workers', async () => {
|
|
308
|
-
pool = new WorkerPool({ enableWorkers: false });
|
|
309
|
-
|
|
310
|
-
// Mock worker for testing
|
|
311
|
-
const mockWorker = { id: 99, terminate: jest.fn().mockResolvedValue(0) };
|
|
312
|
-
pool.workers.push(mockWorker);
|
|
313
|
-
|
|
314
|
-
expect(pool.workers.length).toBe(1);
|
|
315
|
-
|
|
316
|
-
pool.removeWorker(99);
|
|
317
|
-
|
|
318
|
-
expect(pool.workers.length).toBe(0);
|
|
319
|
-
});
|
|
320
|
-
|
|
321
|
-
test('should handle removing non-existent worker', async () => {
|
|
322
|
-
pool = new WorkerPool({ enableWorkers: false });
|
|
323
|
-
|
|
324
|
-
// Should not throw
|
|
325
|
-
pool.removeWorker(999);
|
|
326
|
-
expect(pool.workers.length).toBe(0);
|
|
327
|
-
});
|
|
328
|
-
|
|
329
|
-
test('should restart worker', async () => {
|
|
330
|
-
pool = new WorkerPool({ enableWorkers: false });
|
|
331
|
-
|
|
332
|
-
// Mock worker for testing
|
|
333
|
-
const mockWorker = { id: 99, terminate: jest.fn().mockResolvedValue(0) };
|
|
334
|
-
pool.workers.push(mockWorker);
|
|
335
|
-
|
|
336
|
-
// Restart worker
|
|
337
|
-
pool.restartWorker(99);
|
|
338
|
-
|
|
339
|
-
// Should have called terminate
|
|
340
|
-
expect(mockWorker.terminate).toHaveBeenCalled();
|
|
341
|
-
});
|
|
342
|
-
});
|
|
343
|
-
|
|
344
|
-
describe('error handling in fallbackExecute', () => {
|
|
345
|
-
test('should propagate fallback execution errors', async () => {
|
|
346
|
-
const { default: systemInfo } = await import('systeminformation');
|
|
347
|
-
|
|
348
|
-
pool = new WorkerPool({ enableWorkers: false });
|
|
349
|
-
|
|
350
|
-
systemInfo.currentLoad.mockRejectedValueOnce(new Error('Test error'));
|
|
351
|
-
|
|
352
|
-
await expect(pool.execute('currentLoad')).rejects.toThrow('Test error');
|
|
353
|
-
});
|
|
354
|
-
});
|
|
355
|
-
|
|
356
|
-
describe('handleWorkerMessage', () => {
|
|
357
|
-
test('should handle ready signal', async () => {
|
|
358
|
-
pool = new WorkerPool({ enableWorkers: false });
|
|
359
|
-
|
|
360
|
-
const mockWorker = { id: 1, isReady: false, isBusy: false };
|
|
361
|
-
|
|
362
|
-
pool.handleWorkerMessage(mockWorker, { type: 'ready' });
|
|
363
|
-
|
|
364
|
-
expect(mockWorker.isReady).toBe(true);
|
|
365
|
-
});
|
|
366
|
-
|
|
367
|
-
test('should handle successful task completion', async () => {
|
|
368
|
-
pool = new WorkerPool({ enableWorkers: false });
|
|
369
|
-
|
|
370
|
-
const resolve = jest.fn();
|
|
371
|
-
const reject = jest.fn();
|
|
372
|
-
|
|
373
|
-
pool.pendingTasks.set(1, {
|
|
374
|
-
id: 1,
|
|
375
|
-
command: 'test',
|
|
376
|
-
resolve,
|
|
377
|
-
reject,
|
|
378
|
-
timeout: null,
|
|
379
|
-
});
|
|
380
|
-
|
|
381
|
-
const mockWorker = { id: 1, isBusy: true };
|
|
382
|
-
|
|
383
|
-
pool.handleWorkerMessage(mockWorker, {
|
|
384
|
-
id: 1,
|
|
385
|
-
success: true,
|
|
386
|
-
data: { result: 'test-data' },
|
|
387
|
-
});
|
|
388
|
-
|
|
389
|
-
expect(resolve).toHaveBeenCalledWith({ result: 'test-data' });
|
|
390
|
-
expect(mockWorker.isBusy).toBe(false);
|
|
391
|
-
expect(pool.pendingTasks.has(1)).toBe(false);
|
|
392
|
-
});
|
|
393
|
-
|
|
394
|
-
test('should handle failed task completion', async () => {
|
|
395
|
-
pool = new WorkerPool({ enableWorkers: false });
|
|
396
|
-
|
|
397
|
-
const resolve = jest.fn();
|
|
398
|
-
const reject = jest.fn();
|
|
399
|
-
|
|
400
|
-
pool.pendingTasks.set(1, {
|
|
401
|
-
id: 1,
|
|
402
|
-
command: 'test',
|
|
403
|
-
resolve,
|
|
404
|
-
reject,
|
|
405
|
-
timeout: null,
|
|
406
|
-
});
|
|
407
|
-
|
|
408
|
-
const mockWorker = { id: 1, isBusy: true };
|
|
409
|
-
|
|
410
|
-
pool.handleWorkerMessage(mockWorker, {
|
|
411
|
-
id: 1,
|
|
412
|
-
success: false,
|
|
413
|
-
error: 'Task failed',
|
|
414
|
-
stack: 'Error: Task failed\n at test',
|
|
415
|
-
});
|
|
416
|
-
|
|
417
|
-
expect(reject).toHaveBeenCalled();
|
|
418
|
-
const errorArg = reject.mock.calls[0][0];
|
|
419
|
-
expect(errorArg.message).toBe('Task failed');
|
|
420
|
-
expect(errorArg.stack).toBe('Error: Task failed\n at test');
|
|
421
|
-
expect(mockWorker.isBusy).toBe(false);
|
|
422
|
-
});
|
|
423
|
-
|
|
424
|
-
test('should clear timeout on task completion', async () => {
|
|
425
|
-
pool = new WorkerPool({ enableWorkers: false });
|
|
426
|
-
|
|
427
|
-
const timeout = setTimeout(() => {}, 10000);
|
|
428
|
-
const clearTimeoutSpy = jest.spyOn(global, 'clearTimeout');
|
|
429
|
-
|
|
430
|
-
const resolve = jest.fn();
|
|
431
|
-
|
|
432
|
-
pool.pendingTasks.set(1, {
|
|
433
|
-
id: 1,
|
|
434
|
-
command: 'test',
|
|
435
|
-
resolve,
|
|
436
|
-
reject: jest.fn(),
|
|
437
|
-
timeout,
|
|
438
|
-
});
|
|
439
|
-
|
|
440
|
-
const mockWorker = { id: 1, isBusy: true };
|
|
441
|
-
|
|
442
|
-
pool.handleWorkerMessage(mockWorker, {
|
|
443
|
-
id: 1,
|
|
444
|
-
success: true,
|
|
445
|
-
data: { result: 'test' },
|
|
446
|
-
});
|
|
447
|
-
|
|
448
|
-
expect(clearTimeoutSpy).toHaveBeenCalledWith(timeout);
|
|
449
|
-
|
|
450
|
-
clearTimeoutSpy.mockRestore();
|
|
451
|
-
});
|
|
452
|
-
|
|
453
|
-
test('should ignore messages for unknown task IDs', async () => {
|
|
454
|
-
pool = new WorkerPool({ enableWorkers: false });
|
|
455
|
-
|
|
456
|
-
const mockWorker = { id: 1, isBusy: true };
|
|
457
|
-
|
|
458
|
-
// Should not throw
|
|
459
|
-
pool.handleWorkerMessage(mockWorker, {
|
|
460
|
-
id: 999,
|
|
461
|
-
success: true,
|
|
462
|
-
data: { result: 'test' },
|
|
463
|
-
});
|
|
464
|
-
|
|
465
|
-
// Worker should remain busy since task wasn't found
|
|
466
|
-
expect(mockWorker.isBusy).toBe(true);
|
|
467
|
-
});
|
|
468
|
-
|
|
469
|
-
test('should handle failed task with default error message', async () => {
|
|
470
|
-
pool = new WorkerPool({ enableWorkers: false });
|
|
471
|
-
|
|
472
|
-
const reject = jest.fn();
|
|
473
|
-
|
|
474
|
-
pool.pendingTasks.set(1, {
|
|
475
|
-
id: 1,
|
|
476
|
-
command: 'test',
|
|
477
|
-
resolve: jest.fn(),
|
|
478
|
-
reject,
|
|
479
|
-
timeout: null,
|
|
480
|
-
});
|
|
481
|
-
|
|
482
|
-
const mockWorker = { id: 1, isBusy: true };
|
|
483
|
-
|
|
484
|
-
pool.handleWorkerMessage(mockWorker, {
|
|
485
|
-
id: 1,
|
|
486
|
-
success: false,
|
|
487
|
-
// No error message provided
|
|
488
|
-
});
|
|
489
|
-
|
|
490
|
-
expect(reject).toHaveBeenCalled();
|
|
491
|
-
const errorArg = reject.mock.calls[0][0];
|
|
492
|
-
expect(errorArg.message).toBe('Worker task failed');
|
|
493
|
-
});
|
|
494
|
-
});
|
|
495
|
-
|
|
496
|
-
describe('execute method', () => {
|
|
497
|
-
test('should not set up timeout in fallback mode', async () => {
|
|
498
|
-
pool = new WorkerPool({
|
|
499
|
-
enableWorkers: false,
|
|
500
|
-
taskTimeout: 5000,
|
|
501
|
-
});
|
|
502
|
-
|
|
503
|
-
// Mock setTimeout to verify it's not called in fallback mode
|
|
504
|
-
const setTimeoutSpy = jest.spyOn(global, 'setTimeout');
|
|
505
|
-
|
|
506
|
-
await pool.execute('currentLoad');
|
|
507
|
-
|
|
508
|
-
// In fallback mode, setTimeout should not be called
|
|
509
|
-
expect(setTimeoutSpy).not.toHaveBeenCalled();
|
|
510
|
-
|
|
511
|
-
setTimeoutSpy.mockRestore();
|
|
512
|
-
});
|
|
513
|
-
|
|
514
|
-
test('should use direct execution in fallback mode', async () => {
|
|
515
|
-
pool = new WorkerPool({ enableWorkers: false });
|
|
516
|
-
|
|
517
|
-
const result = await pool.execute('currentLoad');
|
|
518
|
-
|
|
519
|
-
expect(result).toEqual(mockSiData.currentLoad);
|
|
520
|
-
});
|
|
521
|
-
});
|
|
522
|
-
});
|