gaialive commited on
Commit
0d110c2
·
verified ·
1 Parent(s): 0f8492e

Update web/src/utils/testSuite.js

Browse files
Files changed (1) hide show
  1. web/src/utils/testSuite.js +463 -463
web/src/utils/testSuite.js CHANGED
@@ -1,464 +1,464 @@
1
- /**
2
- * GreenPlus by GXS Comprehensive Test Suite
3
- * Automated testing for all environmental analysis tools
4
- */
5
-
6
- import { analyzeAudioForSpecies } from './audioAnalysis';
7
- import { analyzeWaterImage, waterQualityAnalyzer } from './imageAnalysis';
8
- import { waterQualityDB } from './waterQualityDatabase';
9
- import { biodiversityDB } from './biodiversityDatabase';
10
- import { systemHealthChecker } from './systemHealth';
11
- import { accuracyValidator } from './accuracyValidator';
12
-
13
- export class EcoSpireTestSuite {
14
- constructor() {
15
- this.testResults = {
16
- passed: 0,
17
- failed: 0,
18
- warnings: 0,
19
- tests: []
20
- };
21
- }
22
-
23
- /**
24
- * Run all tests
25
- */
26
- async runAllTests() {
27
- console.log('🧪 Starting EcoSpire Test Suite...');
28
-
29
- this.testResults = {
30
- passed: 0,
31
- failed: 0,
32
- warnings: 0,
33
- tests: []
34
- };
35
-
36
- // Core functionality tests
37
- await this.testSystemHealth();
38
- await this.testDatabases();
39
- await this.testAudioAnalysis();
40
- await this.testWaterQualityAnalysis();
41
- await this.testAccuracyValidation();
42
-
43
- // Integration tests
44
- await this.testDataPersistence();
45
- await this.testErrorHandling();
46
- await this.testPerformance();
47
-
48
- // Generate report
49
- const report = this.generateTestReport();
50
- console.log('📊 Test Results:', report);
51
-
52
- return report;
53
- }
54
-
55
- /**
56
- * Test system health monitoring
57
- */
58
- async testSystemHealth() {
59
- const testName = 'System Health Check';
60
- try {
61
- console.log('🔍 Testing system health...');
62
-
63
- const healthStatus = await systemHealthChecker.performHealthCheck();
64
-
65
- this.assert(healthStatus.overall !== 'unknown', 'Health check completed');
66
- this.assert(Object.keys(healthStatus.components).length > 0, 'Components checked');
67
- this.assert(healthStatus.lastCheck !== null, 'Timestamp recorded');
68
-
69
- if (healthStatus.overall === 'error') {
70
- this.addWarning(testName, 'System health shows errors: ' + healthStatus.errors.join(', '));
71
- }
72
-
73
- this.addTest(testName, 'passed', 'System health monitoring working');
74
-
75
- } catch (error) {
76
- this.addTest(testName, 'failed', error.message);
77
- }
78
- }
79
-
80
- /**
81
- * Test database functionality
82
- */
83
- async testDatabases() {
84
- await this.testWaterQualityDB();
85
- await this.testBiodiversityDB();
86
- }
87
-
88
- async testWaterQualityDB() {
89
- const testName = 'Water Quality Database';
90
- try {
91
- console.log('💧 Testing water quality database...');
92
-
93
- // Test initialization
94
- await waterQualityDB.init();
95
- this.assert(waterQualityDB.db !== null, 'Database initialized');
96
-
97
- // Test data operations
98
- const testData = {
99
- id: 'test_' + Date.now(),
100
- waterSource: 'Test Water',
101
- results: { ph: 7.0, chlorine: 1.0 },
102
- overallQuality: 'Good',
103
- safetyLevel: 'Safe',
104
- confidence: 95
105
- };
106
-
107
- await waterQualityDB.saveWaterTest(testData);
108
- const retrieved = await waterQualityDB.getAllWaterTests(1);
109
-
110
- this.assert(retrieved.length > 0, 'Data saved and retrieved');
111
- this.assert(retrieved[0].id === testData.id, 'Data integrity maintained');
112
-
113
- this.addTest(testName, 'passed', 'Database operations working');
114
-
115
- } catch (error) {
116
- this.addTest(testName, 'failed', error.message);
117
- }
118
- }
119
-
120
- async testBiodiversityDB() {
121
- const testName = 'Biodiversity Database';
122
- try {
123
- console.log('🦜 Testing biodiversity database...');
124
-
125
- await biodiversityDB.init();
126
- this.assert(biodiversityDB.db !== null, 'Database initialized');
127
-
128
- const testData = {
129
- id: 'test_bio_' + Date.now(),
130
- habitat: 'Test Habitat',
131
- detectedSpecies: [{ name: 'Test Bird', confidence: 85 }],
132
- biodiversityMetrics: { speciesRichness: 1, ecosystemHealth: 'Good' }
133
- };
134
-
135
- await biodiversityDB.saveRecording(testData);
136
- const retrieved = await biodiversityDB.getAllRecordings(1);
137
-
138
- this.assert(retrieved.length > 0, 'Recording saved and retrieved');
139
-
140
- this.addTest(testName, 'passed', 'Database operations working');
141
-
142
- } catch (error) {
143
- this.addTest(testName, 'failed', error.message);
144
- }
145
- }
146
-
147
- /**
148
- * Test audio analysis functionality
149
- */
150
- async testAudioAnalysis() {
151
- const testName = 'Audio Analysis';
152
- try {
153
- console.log('🎵 Testing audio analysis...');
154
-
155
- // Test with mock data (since we can't generate real audio in tests)
156
- const mockAudioBlob = new Blob(['mock audio data'], { type: 'audio/wav' });
157
-
158
- const result = await analyzeAudioForSpecies(mockAudioBlob, 'North America', 'Urban Park');
159
-
160
- this.assert(result !== null, 'Analysis completed');
161
- this.assert(result.detectedSpecies !== undefined, 'Species detection attempted');
162
- this.assert(result.biodiversityMetrics !== undefined, 'Biodiversity metrics calculated');
163
- this.assert(result.confidence !== undefined, 'Confidence score provided');
164
- this.assert(result.recommendations !== undefined, 'Recommendations generated');
165
-
166
- // Test accuracy validation
167
- const validation = await accuracyValidator.validateBiodiversityAccuracy(result);
168
- this.assert(validation.accuracy !== undefined, 'Accuracy validation working');
169
-
170
- this.addTest(testName, 'passed', 'Audio analysis pipeline working');
171
-
172
- } catch (error) {
173
- this.addTest(testName, 'failed', error.message);
174
- }
175
- }
176
-
177
- /**
178
- * Test water quality analysis
179
- */
180
- async testWaterQualityAnalysis() {
181
- const testName = 'Water Quality Analysis';
182
- try {
183
- console.log('💧 Testing water quality analysis...');
184
-
185
- // Create a mock image (canvas-based)
186
- const canvas = document.createElement('canvas');
187
- canvas.width = 400;
188
- canvas.height = 300;
189
- const ctx = canvas.getContext('2d');
190
-
191
- // Draw a simple test pattern
192
- ctx.fillStyle = '#ff0000';
193
- ctx.fillRect(0, 0, 100, 100);
194
- ctx.fillStyle = '#00ff00';
195
- ctx.fillRect(100, 0, 100, 100);
196
- ctx.fillStyle = '#0000ff';
197
- ctx.fillRect(200, 0, 100, 100);
198
-
199
- const mockImageData = canvas.toDataURL('image/png');
200
-
201
- const result = await analyzeWaterImage(mockImageData, 'Tap Water');
202
-
203
- this.assert(result !== null, 'Analysis completed');
204
- this.assert(result.ph !== undefined, 'pH analysis performed');
205
- this.assert(result.chlorine !== undefined, 'Chlorine analysis performed');
206
- this.assert(result.confidence !== undefined, 'Confidence score provided');
207
-
208
- // Test quality assessment
209
- const assessment = waterQualityAnalyzer.assessWaterQuality(result);
210
- this.assert(assessment.quality !== undefined, 'Quality assessment performed');
211
- this.assert(assessment.safety !== undefined, 'Safety assessment performed');
212
-
213
- this.addTest(testName, 'passed', 'Water quality analysis working');
214
-
215
- } catch (error) {
216
- this.addTest(testName, 'failed', error.message);
217
- }
218
- }
219
-
220
- /**
221
- * Test accuracy validation system
222
- */
223
- async testAccuracyValidation() {
224
- const testName = 'Accuracy Validation';
225
- try {
226
- console.log('🎯 Testing accuracy validation...');
227
-
228
- // Test water quality validation
229
- const mockWaterResult = {
230
- ph: 7.0,
231
- chlorine: 1.5,
232
- nitrates: 5,
233
- hardness: 120,
234
- alkalinity: 100,
235
- bacteria: 0,
236
- confidence: 90
237
- };
238
-
239
- const waterValidation = await accuracyValidator.validateWaterQualityAccuracy(mockWaterResult);
240
- this.assert(waterValidation.accuracy !== undefined, 'Water quality validation working');
241
-
242
- // Test biodiversity validation
243
- const mockBioResult = {
244
- detectedSpecies: [
245
- { name: 'Test Bird', confidence: 85, scientificName: 'Testus birdus', habitat: 'Test' }
246
- ],
247
- biodiversityMetrics: { speciesRichness: 1, ecosystemHealth: 'Good' }
248
- };
249
-
250
- const bioValidation = await accuracyValidator.validateBiodiversityAccuracy(mockBioResult);
251
- this.assert(bioValidation.accuracy !== undefined, 'Biodiversity validation working');
252
-
253
- // Test overall validation
254
- const overallValidation = await accuracyValidator.validateOverallAccuracy();
255
- this.assert(overallValidation.overallAccuracy !== undefined, 'Overall validation working');
256
-
257
- this.addTest(testName, 'passed', 'Accuracy validation system working');
258
-
259
- } catch (error) {
260
- this.addTest(testName, 'failed', error.message);
261
- }
262
- }
263
-
264
- /**
265
- * Test data persistence
266
- */
267
- async testDataPersistence() {
268
- const testName = 'Data Persistence';
269
- try {
270
- console.log('💾 Testing data persistence...');
271
-
272
- // Test localStorage
273
- const testKey = 'ecospire_test_' + Date.now();
274
- const testValue = { test: 'data', timestamp: Date.now() };
275
-
276
- localStorage.setItem(testKey, JSON.stringify(testValue));
277
- const retrieved = JSON.parse(localStorage.getItem(testKey));
278
-
279
- this.assert(retrieved.test === testValue.test, 'localStorage working');
280
-
281
- localStorage.removeItem(testKey);
282
-
283
- // Test IndexedDB (basic check)
284
- this.assert('indexedDB' in window, 'IndexedDB available');
285
-
286
- this.addTest(testName, 'passed', 'Data persistence working');
287
-
288
- } catch (error) {
289
- this.addTest(testName, 'failed', error.message);
290
- }
291
- }
292
-
293
- /**
294
- * Test error handling
295
- */
296
- async testErrorHandling() {
297
- const testName = 'Error Handling';
298
- try {
299
- console.log('⚠️ Testing error handling...');
300
-
301
- // Test invalid audio data
302
- try {
303
- await analyzeAudioForSpecies(null, 'North America', 'Urban Park');
304
- this.addWarning(testName, 'Audio analysis should reject null input');
305
- } catch (error) {
306
- this.assert(error.message.includes('No audio data'), 'Audio error handling working');
307
- }
308
-
309
- // Test invalid image data
310
- try {
311
- await analyzeWaterImage(null, 'Tap Water');
312
- this.addWarning(testName, 'Image analysis should reject null input');
313
- } catch (error) {
314
- this.assert(error.message.includes('No image source'), 'Image error handling working');
315
- }
316
-
317
- this.addTest(testName, 'passed', 'Error handling working');
318
-
319
- } catch (error) {
320
- this.addTest(testName, 'failed', error.message);
321
- }
322
- }
323
-
324
- /**
325
- * Test performance
326
- */
327
- async testPerformance() {
328
- const testName = 'Performance';
329
- try {
330
- console.log('⚡ Testing performance...');
331
-
332
- const startTime = performance.now();
333
-
334
- // Run a quick analysis
335
- const mockAudioBlob = new Blob(['mock'], { type: 'audio/wav' });
336
- await analyzeAudioForSpecies(mockAudioBlob, 'North America', 'Urban Park');
337
-
338
- const endTime = performance.now();
339
- const duration = endTime - startTime;
340
-
341
- this.assert(duration < 10000, 'Analysis completes within 10 seconds'); // Generous limit
342
-
343
- if (duration > 5000) {
344
- this.addWarning(testName, `Analysis took ${duration.toFixed(0)}ms (>5s)`);
345
- }
346
-
347
- this.addTest(testName, 'passed', `Performance acceptable (${duration.toFixed(0)}ms)`);
348
-
349
- } catch (error) {
350
- this.addTest(testName, 'failed', error.message);
351
- }
352
- }
353
-
354
- /**
355
- * Helper methods
356
- */
357
- assert(condition, message) {
358
- if (!condition) {
359
- throw new Error(`Assertion failed: ${message}`);
360
- }
361
- }
362
-
363
- addTest(name, status, message) {
364
- this.testResults.tests.push({ name, status, message, timestamp: new Date().toISOString() });
365
- if (status === 'passed') this.testResults.passed++;
366
- else if (status === 'failed') this.testResults.failed++;
367
- }
368
-
369
- addWarning(testName, message) {
370
- this.testResults.warnings++;
371
- console.warn(`⚠️ ${testName}: ${message}`);
372
- }
373
-
374
- generateTestReport() {
375
- const total = this.testResults.passed + this.testResults.failed;
376
- const successRate = total > 0 ? (this.testResults.passed / total * 100).toFixed(1) : 0;
377
-
378
- return {
379
- summary: {
380
- total: total,
381
- passed: this.testResults.passed,
382
- failed: this.testResults.failed,
383
- warnings: this.testResults.warnings,
384
- successRate: `${successRate}%`
385
- },
386
- details: this.testResults.tests,
387
- timestamp: new Date().toISOString(),
388
- systemReady: this.testResults.failed === 0 && this.testResults.warnings < 3
389
- };
390
- }
391
-
392
- /**
393
- * Run quick health check
394
- */
395
- async quickHealthCheck() {
396
- console.log('🏥 Running quick health check...');
397
-
398
- const checks = {
399
- browserAPIs: this.checkBrowserAPIs(),
400
- localStorage: this.checkLocalStorage(),
401
- databases: await this.checkDatabases(),
402
- utilities: await this.checkUtilities()
403
- };
404
-
405
- const passedChecks = Object.values(checks).filter(Boolean).length;
406
- const totalChecks = Object.keys(checks).length;
407
-
408
- return {
409
- status: passedChecks === totalChecks ? 'healthy' : 'warning',
410
- score: Math.round((passedChecks / totalChecks) * 100),
411
- checks: checks,
412
- message: `${passedChecks}/${totalChecks} health checks passed`
413
- };
414
- }
415
-
416
- checkBrowserAPIs() {
417
- return !!(window.AudioContext || window.webkitAudioContext) &&
418
- !!navigator.mediaDevices &&
419
- !!window.indexedDB &&
420
- !!window.localStorage;
421
- }
422
-
423
- checkLocalStorage() {
424
- try {
425
- localStorage.setItem('test', 'test');
426
- localStorage.removeItem('test');
427
- return true;
428
- } catch (e) {
429
- return false;
430
- }
431
- }
432
-
433
- async checkDatabases() {
434
- try {
435
- await waterQualityDB.init();
436
- await biodiversityDB.init();
437
- return true;
438
- } catch (e) {
439
- return false;
440
- }
441
- }
442
-
443
- async checkUtilities() {
444
- try {
445
- return typeof analyzeAudioForSpecies === 'function' &&
446
- typeof analyzeWaterImage === 'function' &&
447
- typeof systemHealthChecker === 'object';
448
- } catch (e) {
449
- return false;
450
- }
451
- }
452
- }
453
-
454
- // Create singleton instance
455
- export const testSuite = new EcoSpireTestSuite();
456
-
457
- // Auto-run quick health check in development
458
- if (process.env.NODE_ENV === 'development') {
459
- testSuite.quickHealthCheck().then(result => {
460
- console.log('🏥 Quick Health Check:', result.status, `(${result.score}%)`);
461
- });
462
- }
463
-
464
  export default testSuite;
 
1
+ /**
2
+ * GreenPlus by GXS Comprehensive Test Suite
3
+ * Automated testing for all environmental analysis tools
4
+ */
5
+
6
+ import { analyzeAudioForSpecies } from './audioAnalysis';
7
+ import { analyzeWaterImage, waterQualityAnalyzer } from './imageAnalysis';
8
+ import { waterQualityDB } from './waterQualityDatabase';
9
+ import { biodiversityDB } from './biodiversityDatabase';
10
+ import { systemHealthChecker } from './systemHealth';
11
+ import { accuracyValidator } from './accuracyValidator';
12
+
13
+ export class EcoSpireTestSuite {
14
+ constructor() {
15
+ this.testResults = {
16
+ passed: 0,
17
+ failed: 0,
18
+ warnings: 0,
19
+ tests: []
20
+ };
21
+ }
22
+
23
+ /**
24
+ * Run all tests
25
+ */
26
+ async runAllTests() {
27
+ console.log('🧪 Starting GreenPlus by GXS Test Suite...');
28
+
29
+ this.testResults = {
30
+ passed: 0,
31
+ failed: 0,
32
+ warnings: 0,
33
+ tests: []
34
+ };
35
+
36
+ // Core functionality tests
37
+ await this.testSystemHealth();
38
+ await this.testDatabases();
39
+ await this.testAudioAnalysis();
40
+ await this.testWaterQualityAnalysis();
41
+ await this.testAccuracyValidation();
42
+
43
+ // Integration tests
44
+ await this.testDataPersistence();
45
+ await this.testErrorHandling();
46
+ await this.testPerformance();
47
+
48
+ // Generate report
49
+ const report = this.generateTestReport();
50
+ console.log('📊 Test Results:', report);
51
+
52
+ return report;
53
+ }
54
+
55
+ /**
56
+ * Test system health monitoring
57
+ */
58
+ async testSystemHealth() {
59
+ const testName = 'System Health Check';
60
+ try {
61
+ console.log('🔍 Testing system health...');
62
+
63
+ const healthStatus = await systemHealthChecker.performHealthCheck();
64
+
65
+ this.assert(healthStatus.overall !== 'unknown', 'Health check completed');
66
+ this.assert(Object.keys(healthStatus.components).length > 0, 'Components checked');
67
+ this.assert(healthStatus.lastCheck !== null, 'Timestamp recorded');
68
+
69
+ if (healthStatus.overall === 'error') {
70
+ this.addWarning(testName, 'System health shows errors: ' + healthStatus.errors.join(', '));
71
+ }
72
+
73
+ this.addTest(testName, 'passed', 'System health monitoring working');
74
+
75
+ } catch (error) {
76
+ this.addTest(testName, 'failed', error.message);
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Test database functionality
82
+ */
83
+ async testDatabases() {
84
+ await this.testWaterQualityDB();
85
+ await this.testBiodiversityDB();
86
+ }
87
+
88
+ async testWaterQualityDB() {
89
+ const testName = 'Water Quality Database';
90
+ try {
91
+ console.log('💧 Testing water quality database...');
92
+
93
+ // Test initialization
94
+ await waterQualityDB.init();
95
+ this.assert(waterQualityDB.db !== null, 'Database initialized');
96
+
97
+ // Test data operations
98
+ const testData = {
99
+ id: 'test_' + Date.now(),
100
+ waterSource: 'Test Water',
101
+ results: { ph: 7.0, chlorine: 1.0 },
102
+ overallQuality: 'Good',
103
+ safetyLevel: 'Safe',
104
+ confidence: 95
105
+ };
106
+
107
+ await waterQualityDB.saveWaterTest(testData);
108
+ const retrieved = await waterQualityDB.getAllWaterTests(1);
109
+
110
+ this.assert(retrieved.length > 0, 'Data saved and retrieved');
111
+ this.assert(retrieved[0].id === testData.id, 'Data integrity maintained');
112
+
113
+ this.addTest(testName, 'passed', 'Database operations working');
114
+
115
+ } catch (error) {
116
+ this.addTest(testName, 'failed', error.message);
117
+ }
118
+ }
119
+
120
+ async testBiodiversityDB() {
121
+ const testName = 'Biodiversity Database';
122
+ try {
123
+ console.log('🦜 Testing biodiversity database...');
124
+
125
+ await biodiversityDB.init();
126
+ this.assert(biodiversityDB.db !== null, 'Database initialized');
127
+
128
+ const testData = {
129
+ id: 'test_bio_' + Date.now(),
130
+ habitat: 'Test Habitat',
131
+ detectedSpecies: [{ name: 'Test Bird', confidence: 85 }],
132
+ biodiversityMetrics: { speciesRichness: 1, ecosystemHealth: 'Good' }
133
+ };
134
+
135
+ await biodiversityDB.saveRecording(testData);
136
+ const retrieved = await biodiversityDB.getAllRecordings(1);
137
+
138
+ this.assert(retrieved.length > 0, 'Recording saved and retrieved');
139
+
140
+ this.addTest(testName, 'passed', 'Database operations working');
141
+
142
+ } catch (error) {
143
+ this.addTest(testName, 'failed', error.message);
144
+ }
145
+ }
146
+
147
+ /**
148
+ * Test audio analysis functionality
149
+ */
150
+ async testAudioAnalysis() {
151
+ const testName = 'Audio Analysis';
152
+ try {
153
+ console.log('🎵 Testing audio analysis...');
154
+
155
+ // Test with mock data (since we can't generate real audio in tests)
156
+ const mockAudioBlob = new Blob(['mock audio data'], { type: 'audio/wav' });
157
+
158
+ const result = await analyzeAudioForSpecies(mockAudioBlob, 'North America', 'Urban Park');
159
+
160
+ this.assert(result !== null, 'Analysis completed');
161
+ this.assert(result.detectedSpecies !== undefined, 'Species detection attempted');
162
+ this.assert(result.biodiversityMetrics !== undefined, 'Biodiversity metrics calculated');
163
+ this.assert(result.confidence !== undefined, 'Confidence score provided');
164
+ this.assert(result.recommendations !== undefined, 'Recommendations generated');
165
+
166
+ // Test accuracy validation
167
+ const validation = await accuracyValidator.validateBiodiversityAccuracy(result);
168
+ this.assert(validation.accuracy !== undefined, 'Accuracy validation working');
169
+
170
+ this.addTest(testName, 'passed', 'Audio analysis pipeline working');
171
+
172
+ } catch (error) {
173
+ this.addTest(testName, 'failed', error.message);
174
+ }
175
+ }
176
+
177
+ /**
178
+ * Test water quality analysis
179
+ */
180
+ async testWaterQualityAnalysis() {
181
+ const testName = 'Water Quality Analysis';
182
+ try {
183
+ console.log('💧 Testing water quality analysis...');
184
+
185
+ // Create a mock image (canvas-based)
186
+ const canvas = document.createElement('canvas');
187
+ canvas.width = 400;
188
+ canvas.height = 300;
189
+ const ctx = canvas.getContext('2d');
190
+
191
+ // Draw a simple test pattern
192
+ ctx.fillStyle = '#ff0000';
193
+ ctx.fillRect(0, 0, 100, 100);
194
+ ctx.fillStyle = '#00ff00';
195
+ ctx.fillRect(100, 0, 100, 100);
196
+ ctx.fillStyle = '#0000ff';
197
+ ctx.fillRect(200, 0, 100, 100);
198
+
199
+ const mockImageData = canvas.toDataURL('image/png');
200
+
201
+ const result = await analyzeWaterImage(mockImageData, 'Tap Water');
202
+
203
+ this.assert(result !== null, 'Analysis completed');
204
+ this.assert(result.ph !== undefined, 'pH analysis performed');
205
+ this.assert(result.chlorine !== undefined, 'Chlorine analysis performed');
206
+ this.assert(result.confidence !== undefined, 'Confidence score provided');
207
+
208
+ // Test quality assessment
209
+ const assessment = waterQualityAnalyzer.assessWaterQuality(result);
210
+ this.assert(assessment.quality !== undefined, 'Quality assessment performed');
211
+ this.assert(assessment.safety !== undefined, 'Safety assessment performed');
212
+
213
+ this.addTest(testName, 'passed', 'Water quality analysis working');
214
+
215
+ } catch (error) {
216
+ this.addTest(testName, 'failed', error.message);
217
+ }
218
+ }
219
+
220
+ /**
221
+ * Test accuracy validation system
222
+ */
223
+ async testAccuracyValidation() {
224
+ const testName = 'Accuracy Validation';
225
+ try {
226
+ console.log('🎯 Testing accuracy validation...');
227
+
228
+ // Test water quality validation
229
+ const mockWaterResult = {
230
+ ph: 7.0,
231
+ chlorine: 1.5,
232
+ nitrates: 5,
233
+ hardness: 120,
234
+ alkalinity: 100,
235
+ bacteria: 0,
236
+ confidence: 90
237
+ };
238
+
239
+ const waterValidation = await accuracyValidator.validateWaterQualityAccuracy(mockWaterResult);
240
+ this.assert(waterValidation.accuracy !== undefined, 'Water quality validation working');
241
+
242
+ // Test biodiversity validation
243
+ const mockBioResult = {
244
+ detectedSpecies: [
245
+ { name: 'Test Bird', confidence: 85, scientificName: 'Testus birdus', habitat: 'Test' }
246
+ ],
247
+ biodiversityMetrics: { speciesRichness: 1, ecosystemHealth: 'Good' }
248
+ };
249
+
250
+ const bioValidation = await accuracyValidator.validateBiodiversityAccuracy(mockBioResult);
251
+ this.assert(bioValidation.accuracy !== undefined, 'Biodiversity validation working');
252
+
253
+ // Test overall validation
254
+ const overallValidation = await accuracyValidator.validateOverallAccuracy();
255
+ this.assert(overallValidation.overallAccuracy !== undefined, 'Overall validation working');
256
+
257
+ this.addTest(testName, 'passed', 'Accuracy validation system working');
258
+
259
+ } catch (error) {
260
+ this.addTest(testName, 'failed', error.message);
261
+ }
262
+ }
263
+
264
+ /**
265
+ * Test data persistence
266
+ */
267
+ async testDataPersistence() {
268
+ const testName = 'Data Persistence';
269
+ try {
270
+ console.log('💾 Testing data persistence...');
271
+
272
+ // Test localStorage
273
+ const testKey = 'ecospire_test_' + Date.now();
274
+ const testValue = { test: 'data', timestamp: Date.now() };
275
+
276
+ localStorage.setItem(testKey, JSON.stringify(testValue));
277
+ const retrieved = JSON.parse(localStorage.getItem(testKey));
278
+
279
+ this.assert(retrieved.test === testValue.test, 'localStorage working');
280
+
281
+ localStorage.removeItem(testKey);
282
+
283
+ // Test IndexedDB (basic check)
284
+ this.assert('indexedDB' in window, 'IndexedDB available');
285
+
286
+ this.addTest(testName, 'passed', 'Data persistence working');
287
+
288
+ } catch (error) {
289
+ this.addTest(testName, 'failed', error.message);
290
+ }
291
+ }
292
+
293
+ /**
294
+ * Test error handling
295
+ */
296
+ async testErrorHandling() {
297
+ const testName = 'Error Handling';
298
+ try {
299
+ console.log('⚠️ Testing error handling...');
300
+
301
+ // Test invalid audio data
302
+ try {
303
+ await analyzeAudioForSpecies(null, 'North America', 'Urban Park');
304
+ this.addWarning(testName, 'Audio analysis should reject null input');
305
+ } catch (error) {
306
+ this.assert(error.message.includes('No audio data'), 'Audio error handling working');
307
+ }
308
+
309
+ // Test invalid image data
310
+ try {
311
+ await analyzeWaterImage(null, 'Tap Water');
312
+ this.addWarning(testName, 'Image analysis should reject null input');
313
+ } catch (error) {
314
+ this.assert(error.message.includes('No image source'), 'Image error handling working');
315
+ }
316
+
317
+ this.addTest(testName, 'passed', 'Error handling working');
318
+
319
+ } catch (error) {
320
+ this.addTest(testName, 'failed', error.message);
321
+ }
322
+ }
323
+
324
+ /**
325
+ * Test performance
326
+ */
327
+ async testPerformance() {
328
+ const testName = 'Performance';
329
+ try {
330
+ console.log('⚡ Testing performance...');
331
+
332
+ const startTime = performance.now();
333
+
334
+ // Run a quick analysis
335
+ const mockAudioBlob = new Blob(['mock'], { type: 'audio/wav' });
336
+ await analyzeAudioForSpecies(mockAudioBlob, 'North America', 'Urban Park');
337
+
338
+ const endTime = performance.now();
339
+ const duration = endTime - startTime;
340
+
341
+ this.assert(duration < 10000, 'Analysis completes within 10 seconds'); // Generous limit
342
+
343
+ if (duration > 5000) {
344
+ this.addWarning(testName, `Analysis took ${duration.toFixed(0)}ms (>5s)`);
345
+ }
346
+
347
+ this.addTest(testName, 'passed', `Performance acceptable (${duration.toFixed(0)}ms)`);
348
+
349
+ } catch (error) {
350
+ this.addTest(testName, 'failed', error.message);
351
+ }
352
+ }
353
+
354
+ /**
355
+ * Helper methods
356
+ */
357
+ assert(condition, message) {
358
+ if (!condition) {
359
+ throw new Error(`Assertion failed: ${message}`);
360
+ }
361
+ }
362
+
363
+ addTest(name, status, message) {
364
+ this.testResults.tests.push({ name, status, message, timestamp: new Date().toISOString() });
365
+ if (status === 'passed') this.testResults.passed++;
366
+ else if (status === 'failed') this.testResults.failed++;
367
+ }
368
+
369
+ addWarning(testName, message) {
370
+ this.testResults.warnings++;
371
+ console.warn(`⚠️ ${testName}: ${message}`);
372
+ }
373
+
374
+ generateTestReport() {
375
+ const total = this.testResults.passed + this.testResults.failed;
376
+ const successRate = total > 0 ? (this.testResults.passed / total * 100).toFixed(1) : 0;
377
+
378
+ return {
379
+ summary: {
380
+ total: total,
381
+ passed: this.testResults.passed,
382
+ failed: this.testResults.failed,
383
+ warnings: this.testResults.warnings,
384
+ successRate: `${successRate}%`
385
+ },
386
+ details: this.testResults.tests,
387
+ timestamp: new Date().toISOString(),
388
+ systemReady: this.testResults.failed === 0 && this.testResults.warnings < 3
389
+ };
390
+ }
391
+
392
+ /**
393
+ * Run quick health check
394
+ */
395
+ async quickHealthCheck() {
396
+ console.log('🏥 Running quick health check...');
397
+
398
+ const checks = {
399
+ browserAPIs: this.checkBrowserAPIs(),
400
+ localStorage: this.checkLocalStorage(),
401
+ databases: await this.checkDatabases(),
402
+ utilities: await this.checkUtilities()
403
+ };
404
+
405
+ const passedChecks = Object.values(checks).filter(Boolean).length;
406
+ const totalChecks = Object.keys(checks).length;
407
+
408
+ return {
409
+ status: passedChecks === totalChecks ? 'healthy' : 'warning',
410
+ score: Math.round((passedChecks / totalChecks) * 100),
411
+ checks: checks,
412
+ message: `${passedChecks}/${totalChecks} health checks passed`
413
+ };
414
+ }
415
+
416
+ checkBrowserAPIs() {
417
+ return !!(window.AudioContext || window.webkitAudioContext) &&
418
+ !!navigator.mediaDevices &&
419
+ !!window.indexedDB &&
420
+ !!window.localStorage;
421
+ }
422
+
423
+ checkLocalStorage() {
424
+ try {
425
+ localStorage.setItem('test', 'test');
426
+ localStorage.removeItem('test');
427
+ return true;
428
+ } catch (e) {
429
+ return false;
430
+ }
431
+ }
432
+
433
+ async checkDatabases() {
434
+ try {
435
+ await waterQualityDB.init();
436
+ await biodiversityDB.init();
437
+ return true;
438
+ } catch (e) {
439
+ return false;
440
+ }
441
+ }
442
+
443
+ async checkUtilities() {
444
+ try {
445
+ return typeof analyzeAudioForSpecies === 'function' &&
446
+ typeof analyzeWaterImage === 'function' &&
447
+ typeof systemHealthChecker === 'object';
448
+ } catch (e) {
449
+ return false;
450
+ }
451
+ }
452
+ }
453
+
454
+ // Create singleton instance
455
+ export const testSuite = new EcoSpireTestSuite();
456
+
457
+ // Auto-run quick health check in development
458
+ if (process.env.NODE_ENV === 'development') {
459
+ testSuite.quickHealthCheck().then(result => {
460
+ console.log('🏥 Quick Health Check:', result.status, `(${result.score}%)`);
461
+ });
462
+ }
463
+
464
  export default testSuite;