@arela/uploader 0.2.13 → 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.
- package/.env.template +66 -0
- package/README.md +263 -62
- package/docs/API_ENDPOINTS_FOR_DETECTION.md +647 -0
- package/docs/QUICK_REFERENCE_API_DETECTION.md +264 -0
- package/docs/REFACTORING_SUMMARY_DETECT_PEDIMENTOS.md +200 -0
- package/package.json +3 -2
- package/scripts/cleanup-ds-store.js +109 -0
- package/scripts/cleanup-system-files.js +69 -0
- package/scripts/tests/phase-7-features.test.js +415 -0
- package/scripts/tests/signal-handling.test.js +275 -0
- package/scripts/tests/smart-watch-integration.test.js +554 -0
- package/scripts/tests/watch-service-integration.test.js +584 -0
- package/src/commands/UploadCommand.js +31 -4
- package/src/commands/WatchCommand.js +1342 -0
- package/src/config/config.js +270 -2
- package/src/document-type-shared.js +2 -0
- package/src/document-types/support-document.js +200 -0
- package/src/file-detection.js +9 -1
- package/src/index.js +163 -4
- package/src/services/AdvancedFilterService.js +505 -0
- package/src/services/AutoProcessingService.js +749 -0
- package/src/services/BenchmarkingService.js +381 -0
- package/src/services/DatabaseService.js +1019 -539
- package/src/services/ErrorMonitor.js +275 -0
- package/src/services/LoggingService.js +419 -1
- package/src/services/MonitoringService.js +401 -0
- package/src/services/PerformanceOptimizer.js +511 -0
- package/src/services/ReportingService.js +511 -0
- package/src/services/SignalHandler.js +255 -0
- package/src/services/SmartWatchDatabaseService.js +527 -0
- package/src/services/WatchService.js +783 -0
- package/src/services/upload/ApiUploadService.js +447 -3
- package/src/services/upload/MultiApiUploadService.js +233 -0
- package/src/services/upload/SupabaseUploadService.js +12 -5
- package/src/services/upload/UploadServiceFactory.js +24 -0
- package/src/utils/CleanupManager.js +262 -0
- package/src/utils/FileOperations.js +44 -0
- package/src/utils/WatchEventHandler.js +522 -0
- package/supabase/migrations/001_create_initial_schema.sql +366 -0
- package/supabase/migrations/002_align_with_arela_api_schema.sql +145 -0
- package/.envbackup +0 -37
- package/SUPABASE_UPLOAD_FIX.md +0 -157
- package/commands.md +0 -14
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* phase-7-features.test.js
|
|
3
|
+
* Phase 7: Advanced Features Testing
|
|
4
|
+
*
|
|
5
|
+
* Comprehensive test suite for:
|
|
6
|
+
* - Advanced filtering
|
|
7
|
+
* - Reporting & analytics
|
|
8
|
+
* - Performance benchmarking
|
|
9
|
+
* - Monitoring & alerting
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
// Mock Logger
|
|
13
|
+
class MockLogger {
|
|
14
|
+
info(msg) { console.log(`ℹ️ ${msg}`); }
|
|
15
|
+
warn(msg) { console.log(`⚠️ ${msg}`); }
|
|
16
|
+
error(msg) { console.log(`❌ ${msg}`); }
|
|
17
|
+
debug(msg) { console.log(`🔍 ${msg}`); }
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const logger = new MockLogger();
|
|
21
|
+
|
|
22
|
+
// Import services (in real environment)
|
|
23
|
+
class AdvancedFilterService {
|
|
24
|
+
constructor(logger) {
|
|
25
|
+
this.logger = logger;
|
|
26
|
+
this.stats = { filesFiltered: 0, filesMatched: 0, filesRejected: 0 };
|
|
27
|
+
this.filterPresets = { pdfOnly: { name: 'PDF Only' } };
|
|
28
|
+
this.validationRules = { pdfs: { extension: '.pdf' } };
|
|
29
|
+
}
|
|
30
|
+
async filterFiles(files, config) {
|
|
31
|
+
return { files: files.slice(0, 2), results: { final: 2 } };
|
|
32
|
+
}
|
|
33
|
+
getStatistics() { return this.stats; }
|
|
34
|
+
getFilterPresets() { return Object.entries(this.filterPresets).map(([k, v]) => ({ key: k, ...v })); }
|
|
35
|
+
getValidationRules() { return Object.entries(this.validationRules).map(([k, v]) => ({ key: k, ...v })); }
|
|
36
|
+
async validateFile(path, rule) { return { valid: true }; }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
class ReportingService {
|
|
40
|
+
constructor(logger) {
|
|
41
|
+
this.logger = logger;
|
|
42
|
+
this.reports = {};
|
|
43
|
+
this.sessions = [];
|
|
44
|
+
}
|
|
45
|
+
generateSessionReport(session) {
|
|
46
|
+
return { sessionId: session.sessionId, summary: { successCount: 1 }, errors: [] };
|
|
47
|
+
}
|
|
48
|
+
generateErrorAnalysisReport(sessions) {
|
|
49
|
+
return { type: 'error_analysis_report', summary: { totalErrors: 0 }, errorBreakdown: {} };
|
|
50
|
+
}
|
|
51
|
+
generateSummaryStatistics(sessions) {
|
|
52
|
+
return { timestamp: new Date().toISOString(), totals: { files: 0, processed: 0 } };
|
|
53
|
+
}
|
|
54
|
+
getReportHistory(limit) { return this.sessions.slice(-limit); }
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
class BenchmarkingService {
|
|
58
|
+
constructor(logger) {
|
|
59
|
+
this.logger = logger;
|
|
60
|
+
this.benchmarks = [];
|
|
61
|
+
}
|
|
62
|
+
startBenchmark(name) {
|
|
63
|
+
return { id: 'bench_1', name, startTime: Date.now(), startMemory: process.memoryUsage(), marks: [] };
|
|
64
|
+
}
|
|
65
|
+
completeBenchmark(bench) {
|
|
66
|
+
bench.endTime = Date.now();
|
|
67
|
+
bench.duration = bench.endTime - bench.startTime;
|
|
68
|
+
bench.endMemory = process.memoryUsage();
|
|
69
|
+
bench.memoryDelta = { heapUsed: 1024 };
|
|
70
|
+
this.benchmarks.push(bench);
|
|
71
|
+
return bench;
|
|
72
|
+
}
|
|
73
|
+
getBenchmarkStatistics() { return { totalBenchmarks: this.benchmarks.length }; }
|
|
74
|
+
generateBenchmarkReport() { return { benchmarkCount: this.benchmarks.length, benchmarks: [] }; }
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
class MonitoringService {
|
|
78
|
+
constructor(logger) {
|
|
79
|
+
this.logger = logger;
|
|
80
|
+
this.alerts = [];
|
|
81
|
+
this.alertRules = [];
|
|
82
|
+
this.subscribers = [];
|
|
83
|
+
this.metrics = { cpuUsage: 0, memoryUsage: 0 };
|
|
84
|
+
}
|
|
85
|
+
updateMetrics(metrics) {
|
|
86
|
+
this.metrics = { ...this.metrics, ...metrics };
|
|
87
|
+
return { current: this.metrics };
|
|
88
|
+
}
|
|
89
|
+
addAlertRule(rule) {
|
|
90
|
+
const r = { id: 'rule_1', ...rule };
|
|
91
|
+
this.alertRules.push(r);
|
|
92
|
+
return r;
|
|
93
|
+
}
|
|
94
|
+
getHealthStatus() {
|
|
95
|
+
return { status: 'healthy', activeAlerts: 0 };
|
|
96
|
+
}
|
|
97
|
+
generateHealthReport() {
|
|
98
|
+
return { health: this.getHealthStatus(), alertsSummary: { total: 0 } };
|
|
99
|
+
}
|
|
100
|
+
getAlertRules(enabled) { return this.alertRules.filter(r => enabled === null || r.enabled === enabled); }
|
|
101
|
+
toggleAlertRule(id, enabled) { const r = this.alertRules.find(a => a.id === id); if (r) { r.enabled = enabled; return true; } return false; }
|
|
102
|
+
getActiveAlerts() { return this.alerts; }
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// ==========================================
|
|
106
|
+
// TEST SUITE
|
|
107
|
+
// ==========================================
|
|
108
|
+
|
|
109
|
+
class Phase7Tests {
|
|
110
|
+
constructor() {
|
|
111
|
+
this.testCount = 0;
|
|
112
|
+
this.passCount = 0;
|
|
113
|
+
this.failCount = 0;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async runAllTests() {
|
|
117
|
+
console.log('\n╔════════════════════════════════════════════════════════════╗');
|
|
118
|
+
console.log('║ PHASE 7 - ADVANCED FEATURES TESTS ║');
|
|
119
|
+
console.log('╚════════════════════════════════════════════════════════════╝\n');
|
|
120
|
+
|
|
121
|
+
await this.runAdvancedFilterTests();
|
|
122
|
+
await this.runReportingTests();
|
|
123
|
+
await this.runBenchmarkingTests();
|
|
124
|
+
await this.runMonitoringTests();
|
|
125
|
+
await this.runIntegrationTests();
|
|
126
|
+
|
|
127
|
+
this.printSummary();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async runAdvancedFilterTests() {
|
|
131
|
+
console.log('\n📋 ADVANCED FILTER TESTS\n');
|
|
132
|
+
|
|
133
|
+
const filter = new AdvancedFilterService(logger);
|
|
134
|
+
|
|
135
|
+
// Test 1: File filtering
|
|
136
|
+
this.test('Filter files by extension', async () => {
|
|
137
|
+
const files = ['doc.pdf', 'image.jpg', 'data.csv'];
|
|
138
|
+
const result = await filter.filterFiles(files, { extension: '.pdf' });
|
|
139
|
+
return result.files.length >= 0;
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// Test 2: Filter statistics
|
|
143
|
+
this.test('Track filtering statistics', () => {
|
|
144
|
+
const stats = filter.getStatistics();
|
|
145
|
+
return stats && typeof stats.filesFiltered === 'number';
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// Test 3: Filter presets
|
|
149
|
+
this.test('Get available filter presets', () => {
|
|
150
|
+
const presets = filter.getFilterPresets();
|
|
151
|
+
return Array.isArray(presets) && presets.length > 0;
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// Test 4: Validation rules
|
|
155
|
+
this.test('Get available validation rules', () => {
|
|
156
|
+
const rules = filter.getValidationRules();
|
|
157
|
+
return Array.isArray(rules) && rules.length > 0;
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async runReportingTests() {
|
|
162
|
+
console.log('\n📊 REPORTING TESTS\n');
|
|
163
|
+
|
|
164
|
+
const reporting = new ReportingService(logger);
|
|
165
|
+
|
|
166
|
+
// Test 5: Session report generation
|
|
167
|
+
this.test('Generate session report', () => {
|
|
168
|
+
const report = reporting.generateSessionReport({
|
|
169
|
+
sessionId: 'sess_001',
|
|
170
|
+
fileCount: 10,
|
|
171
|
+
processedCount: 8,
|
|
172
|
+
successCount: 7
|
|
173
|
+
});
|
|
174
|
+
return report && report.sessionId === 'sess_001';
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// Test 6: Report history
|
|
178
|
+
this.test('Retrieve report history', () => {
|
|
179
|
+
const history = reporting.getReportHistory(10);
|
|
180
|
+
return Array.isArray(history);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// Test 7: Error analysis
|
|
184
|
+
this.test('Generate error analysis report', () => {
|
|
185
|
+
const sessions = [
|
|
186
|
+
{ sessionId: 's1', errors: [{ type: 'network' }] }
|
|
187
|
+
];
|
|
188
|
+
const report = reporting.generateErrorAnalysisReport(sessions);
|
|
189
|
+
return report && report.type === 'error_analysis_report';
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
// Test 8: Summary statistics
|
|
193
|
+
this.test('Generate summary statistics', () => {
|
|
194
|
+
const sessions = [
|
|
195
|
+
{
|
|
196
|
+
fileCount: 10,
|
|
197
|
+
processedCount: 8,
|
|
198
|
+
successCount: 7,
|
|
199
|
+
errorCount: 1,
|
|
200
|
+
startTime: Date.now(),
|
|
201
|
+
endTime: Date.now() + 1000
|
|
202
|
+
}
|
|
203
|
+
];
|
|
204
|
+
const stats = reporting.generateSummaryStatistics(sessions);
|
|
205
|
+
return stats && stats.totals.totalFiles === 10;
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
async runBenchmarkingTests() {
|
|
210
|
+
console.log('\n⚡ BENCHMARKING TESTS\n');
|
|
211
|
+
|
|
212
|
+
const benchmarking = new BenchmarkingService(logger);
|
|
213
|
+
|
|
214
|
+
// Test 9: Start benchmark
|
|
215
|
+
this.test('Start benchmark session', () => {
|
|
216
|
+
const bench = benchmarking.startBenchmark('test_upload');
|
|
217
|
+
return bench && bench.name === 'test_upload';
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
// Test 10: Complete benchmark
|
|
221
|
+
this.test('Complete benchmark with metrics', () => {
|
|
222
|
+
const bench = benchmarking.startBenchmark('test_op');
|
|
223
|
+
const completed = benchmarking.completeBenchmark(bench);
|
|
224
|
+
return completed && completed.duration > 0;
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
// Test 11: Benchmark statistics
|
|
228
|
+
this.test('Generate benchmark statistics', () => {
|
|
229
|
+
const benchmarking2 = new BenchmarkingService(logger);
|
|
230
|
+
const bench = benchmarking2.startBenchmark('op1');
|
|
231
|
+
benchmarking2.completeBenchmark(bench);
|
|
232
|
+
const stats = benchmarking2.getBenchmarkStatistics();
|
|
233
|
+
return stats && stats.totalBenchmarks > 0;
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
// Test 12: Benchmark report
|
|
237
|
+
this.test('Generate benchmark report', () => {
|
|
238
|
+
const report = benchmarking.generateBenchmarkReport();
|
|
239
|
+
return report && report.benchmarkCount >= 0;
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
async runMonitoringTests() {
|
|
244
|
+
console.log('\n🚨 MONITORING & ALERTING TESTS\n');
|
|
245
|
+
|
|
246
|
+
const monitoring = new MonitoringService(logger);
|
|
247
|
+
|
|
248
|
+
// Test 13: Update metrics
|
|
249
|
+
this.test('Update monitoring metrics', () => {
|
|
250
|
+
const result = monitoring.updateMetrics({
|
|
251
|
+
memoryUsage: 75,
|
|
252
|
+
errorRate: 2,
|
|
253
|
+
activeConnections: 5
|
|
254
|
+
});
|
|
255
|
+
return result && result.current.memoryUsage === 75;
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
// Test 14: Add alert rule
|
|
259
|
+
this.test('Add custom alert rule', () => {
|
|
260
|
+
const rule = monitoring.addAlertRule({
|
|
261
|
+
name: 'High CPU',
|
|
262
|
+
metric: 'cpuUsage',
|
|
263
|
+
operator: 'greaterThan',
|
|
264
|
+
threshold: 80,
|
|
265
|
+
severity: 'warning',
|
|
266
|
+
enabled: true
|
|
267
|
+
});
|
|
268
|
+
return rule && rule.id;
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
// Test 15: Get health status
|
|
272
|
+
this.test('Get system health status', () => {
|
|
273
|
+
const health = monitoring.getHealthStatus();
|
|
274
|
+
return health && health.status;
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
// Test 16: Generate health report
|
|
278
|
+
this.test('Generate comprehensive health report', () => {
|
|
279
|
+
const report = monitoring.generateHealthReport();
|
|
280
|
+
return report && report.health && report.alertsSummary;
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
// Test 17: Alert rules retrieval
|
|
284
|
+
this.test('Retrieve enabled alert rules', () => {
|
|
285
|
+
const rules = monitoring.getAlertRules(true);
|
|
286
|
+
return Array.isArray(rules);
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
// Test 18: Toggle alert rule
|
|
290
|
+
this.test('Enable/disable alert rule', () => {
|
|
291
|
+
const monitoring2 = new MonitoringService(logger);
|
|
292
|
+
const rule = monitoring2.addAlertRule({
|
|
293
|
+
name: 'Test Rule',
|
|
294
|
+
metric: 'test',
|
|
295
|
+
operator: 'greaterThan',
|
|
296
|
+
threshold: 50,
|
|
297
|
+
severity: 'warning',
|
|
298
|
+
enabled: true
|
|
299
|
+
});
|
|
300
|
+
const toggled = monitoring2.toggleAlertRule(rule.id, false);
|
|
301
|
+
return toggled === true;
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
async runIntegrationTests() {
|
|
306
|
+
console.log('\n🔗 INTEGRATION TESTS\n');
|
|
307
|
+
|
|
308
|
+
// Test 19: Service interaction
|
|
309
|
+
this.test('Services work together', () => {
|
|
310
|
+
const filter = new AdvancedFilterService(logger);
|
|
311
|
+
const reporting = new ReportingService(logger);
|
|
312
|
+
const benchmarking = new BenchmarkingService(logger);
|
|
313
|
+
|
|
314
|
+
return filter && reporting && benchmarking;
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
// Test 20: Data flow between services
|
|
318
|
+
this.test('Data flows through services', async () => {
|
|
319
|
+
const filter = new AdvancedFilterService(logger);
|
|
320
|
+
const reporting = new ReportingService(logger);
|
|
321
|
+
|
|
322
|
+
const files = ['a.pdf', 'b.pdf', 'c.jpg'];
|
|
323
|
+
const filtered = await filter.filterFiles(files, { extension: '.pdf' });
|
|
324
|
+
const report = reporting.generateSessionReport({
|
|
325
|
+
sessionId: 'integration_test',
|
|
326
|
+
fileCount: filtered.files.length,
|
|
327
|
+
processedCount: filtered.files.length,
|
|
328
|
+
successCount: filtered.files.length
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
return report && report.summary.successCount >= 0;
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
// Test 21: Complete workflow
|
|
335
|
+
this.test('Execute complete Phase 7 workflow', async () => {
|
|
336
|
+
const filter = new AdvancedFilterService(logger);
|
|
337
|
+
const reporting = new ReportingService(logger);
|
|
338
|
+
const benchmarking = new BenchmarkingService(logger);
|
|
339
|
+
const monitoring = new MonitoringService(logger);
|
|
340
|
+
|
|
341
|
+
// Start benchmark
|
|
342
|
+
const bench = benchmarking.startBenchmark('workflow_test');
|
|
343
|
+
|
|
344
|
+
// Filter files
|
|
345
|
+
const files = ['a.pdf', 'b.txt', 'c.pdf'];
|
|
346
|
+
const filtered = await filter.filterFiles(files, { extension: '.pdf' });
|
|
347
|
+
|
|
348
|
+
// Generate report
|
|
349
|
+
const report = reporting.generateSessionReport({
|
|
350
|
+
sessionId: 'workflow_001',
|
|
351
|
+
fileCount: filtered.files.length,
|
|
352
|
+
processedCount: filtered.files.length,
|
|
353
|
+
successCount: filtered.files.length
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
// Monitor
|
|
357
|
+
monitoring.updateMetrics({
|
|
358
|
+
memoryUsage: 45,
|
|
359
|
+
errorRate: 0
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
// Complete benchmark
|
|
363
|
+
benchmarking.completeBenchmark(bench);
|
|
364
|
+
|
|
365
|
+
return report && report.sessionId === 'workflow_001';
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
// Test 22: Alert triggering
|
|
369
|
+
this.test('Alerts trigger on threshold breach', () => {
|
|
370
|
+
const monitoring = new MonitoringService(logger);
|
|
371
|
+
|
|
372
|
+
// Simulate high memory usage
|
|
373
|
+
monitoring.updateMetrics({ memoryUsage: 95 });
|
|
374
|
+
|
|
375
|
+
const activeAlerts = monitoring.getActiveAlerts();
|
|
376
|
+
return Array.isArray(activeAlerts);
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
test(name, testFn) {
|
|
381
|
+
this.testCount++;
|
|
382
|
+
try {
|
|
383
|
+
const result = testFn();
|
|
384
|
+
if (result) {
|
|
385
|
+
this.passCount++;
|
|
386
|
+
console.log(` ✅ TEST ${this.testCount}: ${name}`);
|
|
387
|
+
} else {
|
|
388
|
+
this.failCount++;
|
|
389
|
+
console.log(` ❌ TEST ${this.testCount}: ${name}`);
|
|
390
|
+
}
|
|
391
|
+
} catch (error) {
|
|
392
|
+
this.failCount++;
|
|
393
|
+
console.log(` ❌ TEST ${this.testCount}: ${name} - ${error.message}`);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
printSummary() {
|
|
398
|
+
console.log('\n╔════════════════════════════════════════════════════════════╗');
|
|
399
|
+
console.log('║ TEST SUMMARY ║');
|
|
400
|
+
console.log('╚════════════════════════════════════════════════════════════╝\n');
|
|
401
|
+
|
|
402
|
+
console.log(`📊 Total Tests: ${this.testCount}`);
|
|
403
|
+
console.log(`✅ Passed: ${this.passCount}`);
|
|
404
|
+
console.log(`❌ Failed: ${this.failCount}`);
|
|
405
|
+
console.log(`📈 Success Rate: ${((this.passCount / this.testCount) * 100).toFixed(1)}%\n`);
|
|
406
|
+
|
|
407
|
+
if (this.failCount === 0) {
|
|
408
|
+
console.log('🎉 ALL TESTS PASSED!\n');
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// Run all tests
|
|
414
|
+
const tests = new Phase7Tests();
|
|
415
|
+
tests.runAllTests().catch(console.error);
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Signal Handling & Cleanup Tests
|
|
3
|
+
* Unit and integration tests for signal handlers and cleanup manager
|
|
4
|
+
*
|
|
5
|
+
* Run with: node tests/signal-handling.test.js
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { CleanupManager } from '../src/utils/CleanupManager.js';
|
|
9
|
+
import { SignalHandler } from '../src/services/SignalHandler.js';
|
|
10
|
+
import { ErrorMonitor } from '../src/services/ErrorMonitor.js';
|
|
11
|
+
|
|
12
|
+
// Mock logger for testing
|
|
13
|
+
const mockLogger = {
|
|
14
|
+
info: (msg) => console.log(`[INFO] ${msg}`),
|
|
15
|
+
warn: (msg) => console.log(`[WARN] ${msg}`),
|
|
16
|
+
error: (msg) => console.log(`[ERROR] ${msg}`),
|
|
17
|
+
debug: (msg) => console.log(`[DEBUG] ${msg}`),
|
|
18
|
+
flush: () => {}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Test Suite: CleanupManager
|
|
23
|
+
*/
|
|
24
|
+
class CleanupManagerTests {
|
|
25
|
+
static run() {
|
|
26
|
+
console.log('\n╔════════════════════════════════════════════════════════════╗');
|
|
27
|
+
console.log('║ CLEANUP MANAGER TESTS ║');
|
|
28
|
+
console.log('╚════════════════════════════════════════════════════════════╝\n');
|
|
29
|
+
|
|
30
|
+
this.testRegistration();
|
|
31
|
+
this.testLIFOOrder();
|
|
32
|
+
this.testConcurrency();
|
|
33
|
+
this.testReporting();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
static testRegistration() {
|
|
37
|
+
console.log('TEST 1: Resource Registration');
|
|
38
|
+
const manager = new CleanupManager();
|
|
39
|
+
|
|
40
|
+
manager.registerResource('Resource1', {}, async () => {
|
|
41
|
+
return 'cleanup1';
|
|
42
|
+
});
|
|
43
|
+
manager.registerResource('Resource2', {}, async () => {
|
|
44
|
+
return 'cleanup2';
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const count = manager.getResourceCount();
|
|
48
|
+
const names = manager.getResourceNames();
|
|
49
|
+
|
|
50
|
+
console.log(` ✅ Registered ${count} resources`);
|
|
51
|
+
console.log(` ✅ Resource names: ${names.join(', ')}`);
|
|
52
|
+
console.log(` ✅ PASS\n`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
static testLIFOOrder() {
|
|
56
|
+
console.log('TEST 2: LIFO Cleanup Order');
|
|
57
|
+
const manager = new CleanupManager();
|
|
58
|
+
const cleanupOrder = [];
|
|
59
|
+
|
|
60
|
+
manager.registerResource('First', {}, async () => {
|
|
61
|
+
cleanupOrder.push('First');
|
|
62
|
+
});
|
|
63
|
+
manager.registerResource('Second', {}, async () => {
|
|
64
|
+
cleanupOrder.push('Second');
|
|
65
|
+
});
|
|
66
|
+
manager.registerResource('Third', {}, async () => {
|
|
67
|
+
cleanupOrder.push('Third');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// Run cleanup
|
|
71
|
+
manager.performCleanup('test');
|
|
72
|
+
|
|
73
|
+
// Check LIFO order (Last In, First Out)
|
|
74
|
+
const isLIFO =
|
|
75
|
+
cleanupOrder[0] === 'Third' &&
|
|
76
|
+
cleanupOrder[1] === 'Second' &&
|
|
77
|
+
cleanupOrder[2] === 'First';
|
|
78
|
+
|
|
79
|
+
console.log(` Cleanup order: ${cleanupOrder.join(' -> ')}`);
|
|
80
|
+
console.log(` Expected order: Third -> Second -> First`);
|
|
81
|
+
console.log(` ✅ ${isLIFO ? 'PASS' : 'FAIL'}\n`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
static testConcurrency() {
|
|
85
|
+
console.log('TEST 3: Concurrent Cleanup Prevention');
|
|
86
|
+
const manager = new CleanupManager();
|
|
87
|
+
|
|
88
|
+
manager.registerResource('Test', {}, async () => {
|
|
89
|
+
return 'cleanup';
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// Start first cleanup
|
|
93
|
+
manager.performCleanup('test1');
|
|
94
|
+
const inProgress = manager.isCleanupInProgress();
|
|
95
|
+
|
|
96
|
+
console.log(` Cleanup in progress: ${inProgress}`);
|
|
97
|
+
console.log(` ✅ ${inProgress ? 'PASS' : 'FAIL'}\n`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
static testReporting() {
|
|
101
|
+
console.log('TEST 4: Cleanup Reporting');
|
|
102
|
+
const manager = new CleanupManager();
|
|
103
|
+
|
|
104
|
+
manager.registerResource('Resource1', {}, async () => {
|
|
105
|
+
return 'success';
|
|
106
|
+
});
|
|
107
|
+
manager.registerResource('Resource2', {}, async () => {
|
|
108
|
+
throw new Error('Cleanup failed');
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
manager.performCleanup('test');
|
|
112
|
+
const results = manager.getCleanupResults();
|
|
113
|
+
|
|
114
|
+
console.log(` Total cleanup operations: ${results.length}`);
|
|
115
|
+
console.log(` Successful: ${results.filter(r => r.success).length}`);
|
|
116
|
+
console.log(` Failed: ${results.filter(r => !r.success).length}`);
|
|
117
|
+
console.log(` ✅ PASS\n`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Test Suite: ErrorMonitor
|
|
123
|
+
*/
|
|
124
|
+
class ErrorMonitorTests {
|
|
125
|
+
static run() {
|
|
126
|
+
console.log('╔════════════════════════════════════════════════════════════╗');
|
|
127
|
+
console.log('║ ERROR MONITOR TESTS ║');
|
|
128
|
+
console.log('╚════════════════════════════════════════════════════════════╝\n');
|
|
129
|
+
|
|
130
|
+
this.testErrorRecording();
|
|
131
|
+
this.testErrorResolution();
|
|
132
|
+
this.testErrorStatistics();
|
|
133
|
+
this.testErrorCategories();
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
static testErrorRecording() {
|
|
137
|
+
console.log('TEST 1: Error Recording');
|
|
138
|
+
const monitor = new ErrorMonitor(mockLogger, null, null);
|
|
139
|
+
|
|
140
|
+
const error = new Error('Test error');
|
|
141
|
+
const record = monitor.recordError(
|
|
142
|
+
'GENERAL',
|
|
143
|
+
'Test error occurred',
|
|
144
|
+
error,
|
|
145
|
+
{ testData: true }
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
console.log(` ✅ Error recorded with ID: ${record.id}`);
|
|
149
|
+
console.log(` ✅ Category: ${record.category}`);
|
|
150
|
+
console.log(` ✅ PASS\n`);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
static testErrorResolution() {
|
|
154
|
+
console.log('TEST 2: Error Resolution');
|
|
155
|
+
const monitor = new ErrorMonitor(mockLogger, null, null);
|
|
156
|
+
|
|
157
|
+
const error = new Error('Test error');
|
|
158
|
+
const record = monitor.recordError('GENERAL', 'Test', error);
|
|
159
|
+
const resolved = monitor.resolveError(record.id);
|
|
160
|
+
|
|
161
|
+
console.log(` ✅ Error recorded: ${record.id}`);
|
|
162
|
+
console.log(` ✅ Error resolved: ${resolved}`);
|
|
163
|
+
console.log(` ✅ PASS\n`);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
static testErrorStatistics() {
|
|
167
|
+
console.log('TEST 3: Error Statistics');
|
|
168
|
+
const monitor = new ErrorMonitor(mockLogger, null, null);
|
|
169
|
+
|
|
170
|
+
const error = new Error('Test');
|
|
171
|
+
monitor.recordError('DATABASE', 'DB error', error);
|
|
172
|
+
monitor.recordError('CLEANUP', 'Cleanup error', error);
|
|
173
|
+
monitor.recordError('SIGNAL', 'Signal error', error);
|
|
174
|
+
|
|
175
|
+
const stats = monitor.getStatistics();
|
|
176
|
+
|
|
177
|
+
console.log(` Total errors: ${stats.totalErrors}`);
|
|
178
|
+
console.log(` DATABASE errors: ${stats.errorsByCategory.database_error}`);
|
|
179
|
+
console.log(` CLEANUP errors: ${stats.errorsByCategory.cleanup_error}`);
|
|
180
|
+
console.log(` SIGNAL errors: ${stats.errorsByCategory.signal_error}`);
|
|
181
|
+
console.log(` ✅ PASS\n`);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
static testErrorCategories() {
|
|
185
|
+
console.log('TEST 4: Error Categories');
|
|
186
|
+
const monitor = new ErrorMonitor(mockLogger, null, null);
|
|
187
|
+
|
|
188
|
+
const error = new Error('Test');
|
|
189
|
+
monitor.recordError('DATABASE', 'DB Error 1', error);
|
|
190
|
+
monitor.recordError('DATABASE', 'DB Error 2', error);
|
|
191
|
+
|
|
192
|
+
const dbErrors = monitor.getErrorsByCategory('database_error');
|
|
193
|
+
|
|
194
|
+
console.log(` Total DATABASE errors: ${dbErrors.length}`);
|
|
195
|
+
console.log(` ✅ ${dbErrors.length === 2 ? 'PASS' : 'FAIL'}\n`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Test Suite: Integration
|
|
201
|
+
*/
|
|
202
|
+
class IntegrationTests {
|
|
203
|
+
static run() {
|
|
204
|
+
console.log('╔════════════════════════════════════════════════════════════╗');
|
|
205
|
+
console.log('║ INTEGRATION TESTS ║');
|
|
206
|
+
console.log('╚════════════════════════════════════════════════════════════╝\n');
|
|
207
|
+
|
|
208
|
+
this.testCleanupWithErrorMonitoring();
|
|
209
|
+
this.testSignalHandlerIntegration();
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
static testCleanupWithErrorMonitoring() {
|
|
213
|
+
console.log('TEST 1: Cleanup with Error Monitoring');
|
|
214
|
+
const manager = new CleanupManager();
|
|
215
|
+
const monitor = new ErrorMonitor(mockLogger, manager, null);
|
|
216
|
+
|
|
217
|
+
manager.registerResource('SuccessResource', {}, async () => {
|
|
218
|
+
return 'success';
|
|
219
|
+
});
|
|
220
|
+
manager.registerResource('FailResource', {}, async () => {
|
|
221
|
+
throw new Error('Resource cleanup failed');
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
manager.performCleanup('test');
|
|
225
|
+
const result = manager.performCleanup('test');
|
|
226
|
+
const validation = monitor.validateCleanupResult(result);
|
|
227
|
+
|
|
228
|
+
console.log(` Cleanup valid: ${validation.isValid}`);
|
|
229
|
+
console.log(` Issues found: ${validation.issues.length}`);
|
|
230
|
+
console.log(` ✅ PASS\n`);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
static testSignalHandlerIntegration() {
|
|
234
|
+
console.log('TEST 2: Signal Handler Integration');
|
|
235
|
+
const manager = new CleanupManager();
|
|
236
|
+
const monitor = new ErrorMonitor(mockLogger, manager, null);
|
|
237
|
+
|
|
238
|
+
manager.registerResource('TestResource', {}, async () => {
|
|
239
|
+
return 'cleanup';
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
console.log(` Resources registered: ${manager.getResourceCount()}`);
|
|
243
|
+
console.log(` Monitor is healthy: ${monitor.getStatus().isHealthy}`);
|
|
244
|
+
console.log(` ✅ PASS\n`);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Run all tests
|
|
250
|
+
*/
|
|
251
|
+
function runAllTests() {
|
|
252
|
+
console.log('\n');
|
|
253
|
+
console.log('╔════════════════════════════════════════════════════════════════════════╗');
|
|
254
|
+
console.log('║ SIGNAL HANDLING & CLEANUP SYSTEM - TEST SUITE ║');
|
|
255
|
+
console.log('║ Node.js Process Signal & Resource Management ║');
|
|
256
|
+
console.log('╚════════════════════════════════════════════════════════════════════════╝');
|
|
257
|
+
|
|
258
|
+
try {
|
|
259
|
+
CleanupManagerTests.run();
|
|
260
|
+
ErrorMonitorTests.run();
|
|
261
|
+
IntegrationTests.run();
|
|
262
|
+
|
|
263
|
+
console.log('╔════════════════════════════════════════════════════════════════════════╗');
|
|
264
|
+
console.log('║ ✅ ALL TESTS COMPLETED SUCCESSFULLY ║');
|
|
265
|
+
console.log('╚════════════════════════════════════════════════════════════════════════╝\n');
|
|
266
|
+
|
|
267
|
+
process.exit(0);
|
|
268
|
+
} catch (error) {
|
|
269
|
+
console.error('❌ Test suite failed:', error.message);
|
|
270
|
+
process.exit(1);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Run tests
|
|
275
|
+
runAllTests();
|