cdp-skill 1.0.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.
@@ -0,0 +1,327 @@
1
+ import { describe, it, mock, beforeEach, afterEach } from 'node:test';
2
+ import assert from 'node:assert';
3
+ import { createErrorAggregator } from '../capture.js';
4
+
5
+ describe('ErrorAggregator', () => {
6
+ let errorAggregator;
7
+ let mockConsoleCapture;
8
+ let mockNetworkCapture;
9
+
10
+ beforeEach(() => {
11
+ mockConsoleCapture = {
12
+ getErrors: mock.fn(() => []),
13
+ getWarnings: mock.fn(() => [])
14
+ };
15
+
16
+ mockNetworkCapture = {
17
+ getNetworkFailures: mock.fn(() => []),
18
+ getHttpErrors: mock.fn(() => []),
19
+ getAllErrors: mock.fn(() => [])
20
+ };
21
+
22
+ errorAggregator = createErrorAggregator(mockConsoleCapture, mockNetworkCapture);
23
+ });
24
+
25
+ afterEach(() => {
26
+ mock.reset();
27
+ });
28
+
29
+ describe('constructor', () => {
30
+ it('should throw error when consoleCapture is null', () => {
31
+ assert.throws(
32
+ () => createErrorAggregator(null, mockNetworkCapture),
33
+ { message: 'consoleCapture is required' }
34
+ );
35
+ });
36
+
37
+ it('should throw error when networkCapture is null', () => {
38
+ assert.throws(
39
+ () => createErrorAggregator(mockConsoleCapture, null),
40
+ { message: 'networkCapture is required' }
41
+ );
42
+ });
43
+
44
+ it('should throw error when consoleCapture is undefined', () => {
45
+ assert.throws(
46
+ () => createErrorAggregator(undefined, mockNetworkCapture),
47
+ { message: 'consoleCapture is required' }
48
+ );
49
+ });
50
+
51
+ it('should throw error when networkCapture is undefined', () => {
52
+ assert.throws(
53
+ () => createErrorAggregator(mockConsoleCapture, undefined),
54
+ { message: 'networkCapture is required' }
55
+ );
56
+ });
57
+ });
58
+
59
+ describe('getSummary', () => {
60
+ it('should return empty summary when no errors', () => {
61
+ const summary = errorAggregator.getSummary();
62
+
63
+ assert.strictEqual(summary.hasErrors, false);
64
+ assert.strictEqual(summary.hasWarnings, false);
65
+ assert.strictEqual(summary.counts.consoleErrors, 0);
66
+ assert.strictEqual(summary.counts.consoleWarnings, 0);
67
+ assert.strictEqual(summary.counts.networkFailures, 0);
68
+ assert.strictEqual(summary.counts.httpClientErrors, 0);
69
+ assert.strictEqual(summary.counts.httpServerErrors, 0);
70
+ });
71
+
72
+ it('should count console errors', () => {
73
+ mockConsoleCapture.getErrors.mock.mockImplementation(() => [
74
+ { level: 'error', text: 'Error 1' },
75
+ { level: 'error', text: 'Error 2' }
76
+ ]);
77
+
78
+ const summary = errorAggregator.getSummary();
79
+
80
+ assert.strictEqual(summary.hasErrors, true);
81
+ assert.strictEqual(summary.counts.consoleErrors, 2);
82
+ });
83
+
84
+ it('should count console warnings', () => {
85
+ mockConsoleCapture.getWarnings.mock.mockImplementation(() => [
86
+ { level: 'warning', text: 'Warning 1' }
87
+ ]);
88
+
89
+ const summary = errorAggregator.getSummary();
90
+
91
+ assert.strictEqual(summary.hasWarnings, true);
92
+ assert.strictEqual(summary.counts.consoleWarnings, 1);
93
+ });
94
+
95
+ it('should count network failures', () => {
96
+ mockNetworkCapture.getNetworkFailures.mock.mockImplementation(() => [
97
+ { type: 'network-failure', errorText: 'Connection refused' }
98
+ ]);
99
+
100
+ const summary = errorAggregator.getSummary();
101
+
102
+ assert.strictEqual(summary.hasErrors, true);
103
+ assert.strictEqual(summary.counts.networkFailures, 1);
104
+ });
105
+
106
+ it('should count HTTP client errors (4xx) as warnings', () => {
107
+ mockNetworkCapture.getHttpErrors.mock.mockImplementation(() => [
108
+ { type: 'http-error', status: 404 },
109
+ { type: 'http-error', status: 403 }
110
+ ]);
111
+
112
+ const summary = errorAggregator.getSummary();
113
+
114
+ assert.strictEqual(summary.hasWarnings, true);
115
+ assert.strictEqual(summary.hasErrors, false);
116
+ assert.strictEqual(summary.counts.httpClientErrors, 2);
117
+ });
118
+
119
+ it('should count HTTP server errors (5xx) as errors', () => {
120
+ mockNetworkCapture.getHttpErrors.mock.mockImplementation(() => [
121
+ { type: 'http-error', status: 500 },
122
+ { type: 'http-error', status: 502 },
123
+ { type: 'http-error', status: 404 }
124
+ ]);
125
+
126
+ const summary = errorAggregator.getSummary();
127
+
128
+ assert.strictEqual(summary.hasErrors, true);
129
+ assert.strictEqual(summary.counts.httpServerErrors, 2);
130
+ assert.strictEqual(summary.counts.httpClientErrors, 1);
131
+ });
132
+
133
+ it('should include error arrays in summary', () => {
134
+ const consoleError = { level: 'error', text: 'Error' };
135
+ const networkFailure = { type: 'network-failure', errorText: 'Failed' };
136
+ const httpError = { type: 'http-error', status: 500 };
137
+
138
+ mockConsoleCapture.getErrors.mock.mockImplementation(() => [consoleError]);
139
+ mockNetworkCapture.getNetworkFailures.mock.mockImplementation(() => [networkFailure]);
140
+ mockNetworkCapture.getHttpErrors.mock.mockImplementation(() => [httpError]);
141
+
142
+ const summary = errorAggregator.getSummary();
143
+
144
+ assert.deepStrictEqual(summary.errors.console, [consoleError]);
145
+ assert.deepStrictEqual(summary.errors.network, [networkFailure]);
146
+ assert.deepStrictEqual(summary.errors.http, [httpError]);
147
+ });
148
+ });
149
+
150
+ describe('getAllErrorsChronological', () => {
151
+ it('should combine and sort all errors by timestamp', () => {
152
+ mockConsoleCapture.getErrors.mock.mockImplementation(() => [
153
+ { level: 'error', text: 'Console error', timestamp: 2000 }
154
+ ]);
155
+
156
+ mockNetworkCapture.getAllErrors.mock.mockImplementation(() => [
157
+ { type: 'network-failure', timestamp: 1000 },
158
+ { type: 'http-error', status: 500, timestamp: 3000 }
159
+ ]);
160
+
161
+ const errors = errorAggregator.getAllErrorsChronological();
162
+
163
+ assert.strictEqual(errors.length, 3);
164
+ assert.strictEqual(errors[0].timestamp, 1000);
165
+ assert.strictEqual(errors[0].source, 'network');
166
+ assert.strictEqual(errors[1].timestamp, 2000);
167
+ assert.strictEqual(errors[1].source, 'console');
168
+ assert.strictEqual(errors[2].timestamp, 3000);
169
+ assert.strictEqual(errors[2].source, 'network');
170
+ });
171
+
172
+ it('should handle errors with missing timestamps', () => {
173
+ mockConsoleCapture.getErrors.mock.mockImplementation(() => [
174
+ { level: 'error', text: 'No timestamp' }
175
+ ]);
176
+
177
+ mockNetworkCapture.getAllErrors.mock.mockImplementation(() => [
178
+ { type: 'network-failure', timestamp: 1000 }
179
+ ]);
180
+
181
+ const errors = errorAggregator.getAllErrorsChronological();
182
+
183
+ assert.strictEqual(errors.length, 2);
184
+ assert.strictEqual(errors[0].timestamp || 0, 0);
185
+ assert.strictEqual(errors[1].timestamp, 1000);
186
+ });
187
+ });
188
+
189
+ describe('getCriticalErrors', () => {
190
+ it('should return only exceptions', () => {
191
+ mockConsoleCapture.getErrors.mock.mockImplementation(() => [
192
+ { type: 'exception', level: 'error', text: 'Exception' },
193
+ { type: 'console', level: 'error', text: 'Console error' }
194
+ ]);
195
+
196
+ const critical = errorAggregator.getCriticalErrors();
197
+
198
+ const consoleErrors = critical.filter(e => e.type === 'exception');
199
+ assert.strictEqual(consoleErrors.length, 1);
200
+ });
201
+
202
+ it('should return network failures', () => {
203
+ mockNetworkCapture.getNetworkFailures.mock.mockImplementation(() => [
204
+ { type: 'network-failure', errorText: 'Connection refused' }
205
+ ]);
206
+
207
+ const critical = errorAggregator.getCriticalErrors();
208
+
209
+ assert.strictEqual(critical.length, 1);
210
+ assert.strictEqual(critical[0].type, 'network-failure');
211
+ });
212
+
213
+ it('should return only 5xx HTTP errors', () => {
214
+ mockNetworkCapture.getHttpErrors.mock.mockImplementation(() => [
215
+ { type: 'http-error', status: 500 },
216
+ { type: 'http-error', status: 404 },
217
+ { type: 'http-error', status: 502 }
218
+ ]);
219
+
220
+ const critical = errorAggregator.getCriticalErrors();
221
+
222
+ assert.strictEqual(critical.length, 2);
223
+ assert.ok(critical.every(e => e.status >= 500));
224
+ });
225
+
226
+ it('should combine all critical error types', () => {
227
+ mockConsoleCapture.getErrors.mock.mockImplementation(() => [
228
+ { type: 'exception', level: 'error', text: 'Exception' }
229
+ ]);
230
+ mockNetworkCapture.getNetworkFailures.mock.mockImplementation(() => [
231
+ { type: 'network-failure', errorText: 'Failed' }
232
+ ]);
233
+ mockNetworkCapture.getHttpErrors.mock.mockImplementation(() => [
234
+ { type: 'http-error', status: 500 }
235
+ ]);
236
+
237
+ const critical = errorAggregator.getCriticalErrors();
238
+
239
+ assert.strictEqual(critical.length, 3);
240
+ });
241
+ });
242
+
243
+ describe('formatReport', () => {
244
+ it('should format empty report', () => {
245
+ const report = errorAggregator.formatReport();
246
+
247
+ assert.ok(report.includes('=== Error Report ==='));
248
+ assert.ok(report.includes('No errors or warnings captured'));
249
+ });
250
+
251
+ it('should format console errors', () => {
252
+ mockConsoleCapture.getErrors.mock.mockImplementation(() => [
253
+ { level: 'error', text: 'Test error', url: 'http://test.com/app.js', line: 42 }
254
+ ]);
255
+
256
+ const report = errorAggregator.formatReport();
257
+
258
+ assert.ok(report.includes('## Console Errors'));
259
+ assert.ok(report.includes('[ERROR] Test error'));
260
+ assert.ok(report.includes('at http://test.com/app.js:42'));
261
+ });
262
+
263
+ it('should format network failures', () => {
264
+ mockNetworkCapture.getNetworkFailures.mock.mockImplementation(() => [
265
+ { method: 'GET', url: 'http://test.com/api', errorText: 'Connection refused' }
266
+ ]);
267
+
268
+ const report = errorAggregator.formatReport();
269
+
270
+ assert.ok(report.includes('## Network Failures'));
271
+ assert.ok(report.includes('[FAILED] GET http://test.com/api'));
272
+ assert.ok(report.includes('Error: Connection refused'));
273
+ });
274
+
275
+ it('should format HTTP errors', () => {
276
+ mockNetworkCapture.getHttpErrors.mock.mockImplementation(() => [
277
+ { status: 404, method: 'GET', url: 'http://test.com/notfound' }
278
+ ]);
279
+
280
+ const report = errorAggregator.formatReport();
281
+
282
+ assert.ok(report.includes('## HTTP Errors'));
283
+ assert.ok(report.includes('[404] GET http://test.com/notfound'));
284
+ });
285
+
286
+ it('should format console error without url', () => {
287
+ mockConsoleCapture.getErrors.mock.mockImplementation(() => [
288
+ { level: 'error', text: 'Test error' }
289
+ ]);
290
+
291
+ const report = errorAggregator.formatReport();
292
+
293
+ assert.ok(report.includes('[ERROR] Test error'));
294
+ assert.ok(!report.includes('at undefined'));
295
+ });
296
+ });
297
+
298
+ describe('toJSON', () => {
299
+ it('should export errors as JSON', () => {
300
+ mockConsoleCapture.getErrors.mock.mockImplementation(() => [
301
+ { level: 'error', text: 'Error', timestamp: 1000 }
302
+ ]);
303
+ mockNetworkCapture.getAllErrors.mock.mockImplementation(() => []);
304
+
305
+ const json = errorAggregator.toJSON();
306
+
307
+ assert.ok(json.timestamp);
308
+ assert.ok(json.summary);
309
+ assert.ok(Array.isArray(json.all));
310
+ });
311
+
312
+ it('should include ISO timestamp', () => {
313
+ const json = errorAggregator.toJSON();
314
+
315
+ assert.ok(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(json.timestamp));
316
+ });
317
+
318
+ it('should include full summary', () => {
319
+ const json = errorAggregator.toJSON();
320
+
321
+ assert.ok('hasErrors' in json.summary);
322
+ assert.ok('hasWarnings' in json.summary);
323
+ assert.ok('counts' in json.summary);
324
+ assert.ok('errors' in json.summary);
325
+ });
326
+ });
327
+ });