claw-dashboard 2.1.0 → 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 +32 -42
- package/package.json +23 -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/src/widgets/builtin-widgets.js +0 -94
- package/src/widgets/index.js +0 -1
- 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/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
package/tests/alerts.test.js
DELETED
|
@@ -1,437 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unit tests for alert threshold logic in alerts.js
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import alerts from '../src/alerts.js';
|
|
6
|
-
|
|
7
|
-
describe('Alert Threshold Logic', () => {
|
|
8
|
-
beforeEach(() => {
|
|
9
|
-
// Reset thresholds and clear alerts before each test
|
|
10
|
-
alerts.resetThresholds();
|
|
11
|
-
alerts.clearAllAlerts();
|
|
12
|
-
alerts.resetRateLimit();
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
describe('checkThreshold', () => {
|
|
16
|
-
test('should return null when value is below warning threshold', () => {
|
|
17
|
-
const result = alerts.checkThreshold('cpu', 50);
|
|
18
|
-
expect(result).toBeNull();
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
test('should create warning alert when value reaches warning threshold', () => {
|
|
22
|
-
const result = alerts.checkThreshold('cpu', 70);
|
|
23
|
-
|
|
24
|
-
expect(result).not.toBeNull();
|
|
25
|
-
expect(result.type).toBe('cpu');
|
|
26
|
-
expect(result.level).toBe(alerts.AlertLevel.WARNING);
|
|
27
|
-
expect(result.value).toBe(70);
|
|
28
|
-
expect(result.threshold).toBe(70);
|
|
29
|
-
expect(result.message).toContain('Warning');
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
test('should create critical alert when value reaches critical threshold', () => {
|
|
33
|
-
const result = alerts.checkThreshold('cpu', 90);
|
|
34
|
-
|
|
35
|
-
expect(result).not.toBeNull();
|
|
36
|
-
expect(result.type).toBe('cpu');
|
|
37
|
-
expect(result.level).toBe(alerts.AlertLevel.CRITICAL);
|
|
38
|
-
expect(result.value).toBe(90);
|
|
39
|
-
expect(result.threshold).toBe(90);
|
|
40
|
-
expect(result.message).toContain('Critical');
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
test('should upgrade warning to critical when value crosses threshold', () => {
|
|
44
|
-
// First create a warning
|
|
45
|
-
alerts.checkThreshold('cpu', 70);
|
|
46
|
-
const criticalAlert = alerts.checkThreshold('cpu', 90);
|
|
47
|
-
|
|
48
|
-
expect(criticalAlert).not.toBeNull();
|
|
49
|
-
expect(criticalAlert.level).toBe(alerts.AlertLevel.CRITICAL);
|
|
50
|
-
|
|
51
|
-
// Should have critical alert in active alerts
|
|
52
|
-
const activeAlerts = alerts.getActiveAlerts();
|
|
53
|
-
const cpuAlert = activeAlerts.find(a => a.type === 'cpu');
|
|
54
|
-
expect(cpuAlert.level).toBe(alerts.AlertLevel.CRITICAL);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
test('should not create duplicate alerts for same level', () => {
|
|
58
|
-
alerts.checkThreshold('cpu', 75);
|
|
59
|
-
const activeBefore = alerts.getActiveAlerts().length;
|
|
60
|
-
|
|
61
|
-
alerts.checkThreshold('cpu', 78);
|
|
62
|
-
const activeAfter = alerts.getActiveAlerts().length;
|
|
63
|
-
|
|
64
|
-
// Should not add duplicate - just update existing
|
|
65
|
-
expect(activeBefore).toBe(activeAfter);
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
test('should create cleared alert when value drops below threshold', () => {
|
|
69
|
-
// Create a warning alert first
|
|
70
|
-
alerts.checkThreshold('cpu', 75);
|
|
71
|
-
|
|
72
|
-
// Clear the alert by dropping below threshold
|
|
73
|
-
const clearedAlert = alerts.checkThreshold('cpu', 50);
|
|
74
|
-
|
|
75
|
-
expect(clearedAlert).not.toBeNull();
|
|
76
|
-
expect(clearedAlert.level).toBe(alerts.AlertLevel.CLEARED);
|
|
77
|
-
expect(clearedAlert.message).toContain('normalized');
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
test('should return null when updating existing alert with same level', () => {
|
|
81
|
-
alerts.checkThreshold('cpu', 75);
|
|
82
|
-
const result = alerts.checkThreshold('cpu', 80);
|
|
83
|
-
|
|
84
|
-
// Should update but return null (no new alert)
|
|
85
|
-
expect(result).toBeNull();
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
test('should handle memory metric type', () => {
|
|
89
|
-
const result = alerts.checkThreshold('memory', 80);
|
|
90
|
-
|
|
91
|
-
expect(result).not.toBeNull();
|
|
92
|
-
expect(result.type).toBe('memory');
|
|
93
|
-
expect(result.level).toBe(alerts.AlertLevel.WARNING);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
test('should handle disk metric type', () => {
|
|
97
|
-
const result = alerts.checkThreshold('disk', 85);
|
|
98
|
-
|
|
99
|
-
expect(result).not.toBeNull();
|
|
100
|
-
expect(result.type).toBe('disk');
|
|
101
|
-
expect(result.level).toBe(alerts.AlertLevel.WARNING);
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
test('should return null for unknown metric type', () => {
|
|
105
|
-
const result = alerts.checkThreshold('unknown', 50);
|
|
106
|
-
expect(result).toBeNull();
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
test('should use custom thresholds when set', () => {
|
|
110
|
-
alerts.setThresholds({
|
|
111
|
-
cpu: { warning: 50, critical: 80 }
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
// Should trigger warning at 50
|
|
115
|
-
const warningResult = alerts.checkThreshold('cpu', 50);
|
|
116
|
-
expect(warningResult.level).toBe(alerts.AlertLevel.WARNING);
|
|
117
|
-
|
|
118
|
-
// Should trigger critical at 80
|
|
119
|
-
const criticalResult = alerts.checkThreshold('cpu', 80);
|
|
120
|
-
expect(criticalResult.level).toBe(alerts.AlertLevel.CRITICAL);
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
test('should handle edge case at exact threshold values', () => {
|
|
124
|
-
// Reset to ensure clean state
|
|
125
|
-
alerts.clearAllAlerts();
|
|
126
|
-
|
|
127
|
-
// Exact warning threshold
|
|
128
|
-
const warningResult = alerts.checkThreshold('cpu', 70);
|
|
129
|
-
expect(warningResult.level).toBe(alerts.AlertLevel.WARNING);
|
|
130
|
-
|
|
131
|
-
// Exact critical threshold
|
|
132
|
-
const criticalResult = alerts.checkThreshold('cpu', 90);
|
|
133
|
-
expect(criticalResult.level).toBe(alerts.AlertLevel.CRITICAL);
|
|
134
|
-
|
|
135
|
-
// Clear the alert - value below warning but existing critical alert exists
|
|
136
|
-
const clearedResult = alerts.checkThreshold('cpu', 69);
|
|
137
|
-
// Should create a cleared alert since there was an active critical alert
|
|
138
|
-
expect(clearedResult).not.toBeNull();
|
|
139
|
-
expect(clearedResult.level).toBe(alerts.AlertLevel.CLEARED);
|
|
140
|
-
});
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
describe('checkAllMetrics', () => {
|
|
144
|
-
test('should check all provided metrics', () => {
|
|
145
|
-
const metrics = {
|
|
146
|
-
cpu: 85,
|
|
147
|
-
memory: 80,
|
|
148
|
-
disk: 90
|
|
149
|
-
};
|
|
150
|
-
|
|
151
|
-
const results = alerts.checkAllMetrics(metrics);
|
|
152
|
-
|
|
153
|
-
expect(results.length).toBe(3);
|
|
154
|
-
expect(results.map(r => r.type)).toContain('cpu');
|
|
155
|
-
expect(results.map(r => r.type)).toContain('memory');
|
|
156
|
-
expect(results.map(r => r.type)).toContain('disk');
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
test('should only check metrics that are defined', () => {
|
|
160
|
-
const metrics = {
|
|
161
|
-
cpu: 85
|
|
162
|
-
// memory and disk not provided
|
|
163
|
-
};
|
|
164
|
-
|
|
165
|
-
const results = alerts.checkAllMetrics(metrics);
|
|
166
|
-
|
|
167
|
-
expect(results.length).toBe(1);
|
|
168
|
-
expect(results[0].type).toBe('cpu');
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
test('should return empty array when no metrics exceed thresholds', () => {
|
|
172
|
-
const metrics = {
|
|
173
|
-
cpu: 50,
|
|
174
|
-
memory: 50,
|
|
175
|
-
disk: 50
|
|
176
|
-
};
|
|
177
|
-
|
|
178
|
-
const results = alerts.checkAllMetrics(metrics);
|
|
179
|
-
|
|
180
|
-
expect(results.length).toBe(0);
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
test('should handle undefined metrics object', () => {
|
|
184
|
-
const results = alerts.checkAllMetrics(undefined);
|
|
185
|
-
expect(results).toEqual([]);
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
test('should handle empty metrics object', () => {
|
|
189
|
-
const results = alerts.checkAllMetrics({});
|
|
190
|
-
expect(results).toEqual([]);
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
test('should return only new alerts, not updates', () => {
|
|
194
|
-
const metrics1 = { cpu: 75 };
|
|
195
|
-
const results1 = alerts.checkAllMetrics(metrics1);
|
|
196
|
-
expect(results1.length).toBe(1);
|
|
197
|
-
|
|
198
|
-
// Update same metric
|
|
199
|
-
const metrics2 = { cpu: 80 };
|
|
200
|
-
const results2 = alerts.checkAllMetrics(metrics2);
|
|
201
|
-
// This returns null for updates, so should be 0
|
|
202
|
-
expect(results2.length).toBe(0);
|
|
203
|
-
});
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
describe('Alert Level Constants', () => {
|
|
207
|
-
test('should have correct alert level values', () => {
|
|
208
|
-
expect(alerts.AlertLevel.INFO).toBe('info');
|
|
209
|
-
expect(alerts.AlertLevel.WARNING).toBe('warning');
|
|
210
|
-
expect(alerts.AlertLevel.CRITICAL).toBe('critical');
|
|
211
|
-
expect(alerts.AlertLevel.CLEARED).toBe('cleared');
|
|
212
|
-
});
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
describe('Threshold Management', () => {
|
|
216
|
-
test('getThresholds should return current thresholds', () => {
|
|
217
|
-
const thresholds = alerts.getThresholds();
|
|
218
|
-
|
|
219
|
-
expect(thresholds.cpu.warning).toBe(70);
|
|
220
|
-
expect(thresholds.cpu.critical).toBe(90);
|
|
221
|
-
expect(thresholds.memory.warning).toBe(75);
|
|
222
|
-
expect(thresholds.memory.critical).toBe(90);
|
|
223
|
-
expect(thresholds.disk.warning).toBe(80);
|
|
224
|
-
expect(thresholds.disk.critical).toBe(95);
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
test('resetThresholds should restore defaults', () => {
|
|
228
|
-
alerts.setThresholds({ cpu: { warning: 10, critical: 20 } });
|
|
229
|
-
alerts.resetThresholds();
|
|
230
|
-
|
|
231
|
-
const thresholds = alerts.getThresholds();
|
|
232
|
-
expect(thresholds.cpu.warning).toBe(70);
|
|
233
|
-
expect(thresholds.cpu.critical).toBe(90);
|
|
234
|
-
});
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
describe('Alert Management', () => {
|
|
238
|
-
test('getActiveAlerts should return non-dismissed alerts', () => {
|
|
239
|
-
alerts.checkThreshold('cpu', 85);
|
|
240
|
-
alerts.checkThreshold('memory', 85);
|
|
241
|
-
|
|
242
|
-
const active = alerts.getActiveAlerts();
|
|
243
|
-
expect(active.length).toBe(2);
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
test('dismissAlert should mark alert as dismissed', () => {
|
|
247
|
-
const alert = alerts.checkThreshold('cpu', 85);
|
|
248
|
-
alerts.dismissAlert(alert.id);
|
|
249
|
-
|
|
250
|
-
const active = alerts.getActiveAlerts();
|
|
251
|
-
expect(active.find(a => a.id === alert.id)).toBeUndefined();
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
test('getAlertsByLevel should filter by level', () => {
|
|
255
|
-
alerts.checkThreshold('cpu', 95); // critical (above 90%)
|
|
256
|
-
alerts.checkThreshold('memory', 75); // warning
|
|
257
|
-
|
|
258
|
-
const critical = alerts.getAlertsByLevel(alerts.AlertLevel.CRITICAL);
|
|
259
|
-
expect(critical.length).toBe(1);
|
|
260
|
-
expect(critical[0].type).toBe('cpu');
|
|
261
|
-
|
|
262
|
-
const warning = alerts.getAlertsByLevel(alerts.AlertLevel.WARNING);
|
|
263
|
-
expect(warning.length).toBe(1);
|
|
264
|
-
expect(warning[0].type).toBe('memory');
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
test('clearAllAlerts should dismiss all active alerts', () => {
|
|
268
|
-
alerts.checkThreshold('cpu', 85);
|
|
269
|
-
alerts.checkThreshold('memory', 85);
|
|
270
|
-
|
|
271
|
-
alerts.clearAllAlerts();
|
|
272
|
-
|
|
273
|
-
const active = alerts.getActiveAlerts();
|
|
274
|
-
expect(active.length).toBe(0);
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
test('getAlertHistory should return all alerts including dismissed', () => {
|
|
278
|
-
const alert = alerts.checkThreshold('cpu', 85);
|
|
279
|
-
alerts.dismissAlert(alert.id);
|
|
280
|
-
|
|
281
|
-
const history = alerts.getAlertHistory();
|
|
282
|
-
expect(history.length).toBeGreaterThan(0);
|
|
283
|
-
});
|
|
284
|
-
});
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
describe('Rate Limiting', () => {
|
|
288
|
-
beforeEach(() => {
|
|
289
|
-
alerts.resetThresholds();
|
|
290
|
-
alerts.clearAllAlerts();
|
|
291
|
-
alerts.resetRateLimit();
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
describe('shouldRateLimitAlert', () => {
|
|
295
|
-
test('should allow alerts when rate limiting is disabled', () => {
|
|
296
|
-
alerts.setRateLimit({ enabled: false });
|
|
297
|
-
|
|
298
|
-
// Should never rate limit when disabled
|
|
299
|
-
for (let i = 0; i < 10; i++) {
|
|
300
|
-
expect(alerts.shouldRateLimitAlert('cpu')).toBe(false);
|
|
301
|
-
}
|
|
302
|
-
});
|
|
303
|
-
|
|
304
|
-
test('should allow first few alerts within limit', () => {
|
|
305
|
-
alerts.setRateLimit({ enabled: true, maxAlerts: 3, windowMs: 60000 });
|
|
306
|
-
|
|
307
|
-
expect(alerts.shouldRateLimitAlert('cpu')).toBe(false);
|
|
308
|
-
expect(alerts.shouldRateLimitAlert('cpu')).toBe(false);
|
|
309
|
-
expect(alerts.shouldRateLimitAlert('cpu')).toBe(false);
|
|
310
|
-
});
|
|
311
|
-
|
|
312
|
-
test('should rate limit alerts exceeding maxAlerts', () => {
|
|
313
|
-
alerts.setRateLimit({ enabled: true, maxAlerts: 2, windowMs: 60000 });
|
|
314
|
-
|
|
315
|
-
// First two should pass (check + record)
|
|
316
|
-
expect(alerts.shouldRateLimitAlert('cpu')).toBe(false);
|
|
317
|
-
alerts.recordAlertTimestamp('cpu');
|
|
318
|
-
|
|
319
|
-
expect(alerts.shouldRateLimitAlert('cpu')).toBe(false);
|
|
320
|
-
alerts.recordAlertTimestamp('cpu');
|
|
321
|
-
|
|
322
|
-
// Third should be rate limited
|
|
323
|
-
expect(alerts.shouldRateLimitAlert('cpu')).toBe(true);
|
|
324
|
-
});
|
|
325
|
-
|
|
326
|
-
test('should track rate limits separately per alert type', () => {
|
|
327
|
-
alerts.setRateLimit({ enabled: true, maxAlerts: 1, windowMs: 60000 });
|
|
328
|
-
|
|
329
|
-
// cpu should be limited after one
|
|
330
|
-
expect(alerts.shouldRateLimitAlert('cpu')).toBe(false);
|
|
331
|
-
alerts.recordAlertTimestamp('cpu');
|
|
332
|
-
|
|
333
|
-
expect(alerts.shouldRateLimitAlert('cpu')).toBe(true);
|
|
334
|
-
|
|
335
|
-
// memory should still be allowed
|
|
336
|
-
expect(alerts.shouldRateLimitAlert('memory')).toBe(false);
|
|
337
|
-
});
|
|
338
|
-
|
|
339
|
-
test('should respect custom windowMs', async () => {
|
|
340
|
-
alerts.setRateLimit({ enabled: true, maxAlerts: 1, windowMs: 50 });
|
|
341
|
-
|
|
342
|
-
expect(alerts.shouldRateLimitAlert('cpu')).toBe(false);
|
|
343
|
-
alerts.recordAlertTimestamp('cpu');
|
|
344
|
-
|
|
345
|
-
expect(alerts.shouldRateLimitAlert('cpu')).toBe(true);
|
|
346
|
-
|
|
347
|
-
// Wait for window to expire
|
|
348
|
-
await new Promise(resolve => setTimeout(resolve, 60));
|
|
349
|
-
|
|
350
|
-
// Should be allowed again after window expires
|
|
351
|
-
expect(alerts.shouldRateLimitAlert('cpu')).toBe(false);
|
|
352
|
-
});
|
|
353
|
-
});
|
|
354
|
-
|
|
355
|
-
describe('checkThreshold with rate limiting', () => {
|
|
356
|
-
test('should suppress warning alerts when rate limited', () => {
|
|
357
|
-
alerts.setRateLimit({ enabled: true, maxAlerts: 1, windowMs: 60000 });
|
|
358
|
-
|
|
359
|
-
// First alert goes through
|
|
360
|
-
const alert1 = alerts.checkThreshold('cpu', 75);
|
|
361
|
-
expect(alert1).not.toBeNull();
|
|
362
|
-
|
|
363
|
-
// Second warning should be suppressed
|
|
364
|
-
const alert2 = alerts.checkThreshold('cpu', 80);
|
|
365
|
-
expect(alert2).toBeNull();
|
|
366
|
-
});
|
|
367
|
-
|
|
368
|
-
test('should always allow critical alerts even when rate limited', () => {
|
|
369
|
-
alerts.setRateLimit({ enabled: true, maxAlerts: 1, windowMs: 60000 });
|
|
370
|
-
|
|
371
|
-
// First alert (warning) goes through
|
|
372
|
-
const alert1 = alerts.checkThreshold('cpu', 75);
|
|
373
|
-
expect(alert1).not.toBeNull();
|
|
374
|
-
|
|
375
|
-
// Critical should always go through
|
|
376
|
-
const alert2 = alerts.checkThreshold('cpu', 95);
|
|
377
|
-
expect(alert2).not.toBeNull();
|
|
378
|
-
expect(alert2.level).toBe(alerts.AlertLevel.CRITICAL);
|
|
379
|
-
});
|
|
380
|
-
|
|
381
|
-
test('should always allow cleared alerts', () => {
|
|
382
|
-
alerts.setRateLimit({ enabled: true, maxAlerts: 1, windowMs: 60000 });
|
|
383
|
-
|
|
384
|
-
// Create an alert
|
|
385
|
-
alerts.checkThreshold('cpu', 85);
|
|
386
|
-
|
|
387
|
-
// Clear the alert - should always go through
|
|
388
|
-
const cleared = alerts.checkThreshold('cpu', 50);
|
|
389
|
-
expect(cleared).not.toBeNull();
|
|
390
|
-
expect(cleared.level).toBe(alerts.AlertLevel.CLEARED);
|
|
391
|
-
});
|
|
392
|
-
});
|
|
393
|
-
|
|
394
|
-
describe('getRateLimit and setRateLimit', () => {
|
|
395
|
-
test('should return current rate limit config', () => {
|
|
396
|
-
const config = alerts.getRateLimit();
|
|
397
|
-
|
|
398
|
-
expect(config.enabled).toBe(true);
|
|
399
|
-
expect(config.windowMs).toBe(60000);
|
|
400
|
-
expect(config.maxAlerts).toBe(5);
|
|
401
|
-
});
|
|
402
|
-
|
|
403
|
-
test('should update rate limit config', () => {
|
|
404
|
-
alerts.setRateLimit({ enabled: false, maxAlerts: 10 });
|
|
405
|
-
|
|
406
|
-
const config = alerts.getRateLimit();
|
|
407
|
-
expect(config.enabled).toBe(false);
|
|
408
|
-
expect(config.maxAlerts).toBe(10);
|
|
409
|
-
});
|
|
410
|
-
|
|
411
|
-
test('should preserve unspecified values when setting config', () => {
|
|
412
|
-
alerts.setRateLimit({ maxAlerts: 3 });
|
|
413
|
-
|
|
414
|
-
const config = alerts.getRateLimit();
|
|
415
|
-
expect(config.maxAlerts).toBe(3);
|
|
416
|
-
expect(config.enabled).toBe(true); // preserved
|
|
417
|
-
expect(config.windowMs).toBe(60000); // preserved
|
|
418
|
-
});
|
|
419
|
-
});
|
|
420
|
-
|
|
421
|
-
describe('resetRateLimit', () => {
|
|
422
|
-
test('should reset rate limit state', () => {
|
|
423
|
-
alerts.setRateLimit({ enabled: true, maxAlerts: 1, windowMs: 60000 });
|
|
424
|
-
|
|
425
|
-
// Trigger rate limit
|
|
426
|
-
alerts.shouldRateLimitAlert('cpu');
|
|
427
|
-
alerts.shouldRateLimitAlert('cpu');
|
|
428
|
-
|
|
429
|
-
// Reset
|
|
430
|
-
alerts.resetRateLimit();
|
|
431
|
-
|
|
432
|
-
const config = alerts.getRateLimit();
|
|
433
|
-
expect(config.enabled).toBe(true);
|
|
434
|
-
expect(config.maxAlerts).toBe(5); // back to default
|
|
435
|
-
});
|
|
436
|
-
});
|
|
437
|
-
});
|