#!/usr/bin/env node /** * Comprehensive testing script for Phase 2 improvements * Tests connection pooling, database indexes, rate limiting, and analytics */ const fs = require('fs'); const path = require('path'); const { execSync } = require('child_process'); // Configuration const BACKEND_DIR = path.join(__dirname, '..', 'src'); const MIGRATIONS_DIR = path.join(BACKEND_DIR, 'models', 'migrations'); // Test results const testResults = { connectionPooling: { passed: false, details: [] }, databaseIndexes: { passed: false, details: [] }, rateLimiting: { passed: false, details: [] }, analyticsImplementation: { passed: false, details: [] }, overall: { passed: false, score: 0 } }; console.log('๐Ÿงช Testing Phase 2 Improvements...\n'); // Test 1: Connection Pooling function testConnectionPooling() { console.log('๐Ÿ”— Testing connection pooling...'); try { const supabaseFile = path.join(BACKEND_DIR, 'config', 'supabase.ts'); const content = fs.readFileSync(supabaseFile, 'utf8'); const checks = [ { name: 'Connection manager class', pattern: /class SupabaseConnectionManager/g, min: 1 }, { name: 'Connection pool configuration', pattern: /maxConnections/g, min: 1 }, { name: 'Pool cleanup mechanism', pattern: /cleanupStaleConnections/g, min: 1 }, { name: 'Pooled client functions', pattern: /getPooledClient/g, min: 1 }, { name: 'Connection stats', pattern: /getConnectionStats/g, min: 1 }, { name: 'Graceful shutdown', pattern: /shutdownSupabase/g, min: 1 }, { name: 'Connection reuse logic', pattern: /connection_reuse/g, min: 1 }, { name: 'Pool management', pattern: /pools\.set/g, min: 1 }, ]; let passedChecks = 0; for (const check of checks) { const matches = content.match(check.pattern); if (matches && matches.length >= check.min) { passedChecks++; testResults.connectionPooling.details.push(`${check.name}: ${matches.length} found`); } else { testResults.connectionPooling.details.push(`${check.name}: ${matches?.length || 0} found (expected ${check.min}+)`); } } if (passedChecks >= 6) { testResults.connectionPooling.passed = true; console.log(`โœ… Connection pooling: ${passedChecks}/8 checks passed`); } else { console.log(`โŒ Connection pooling: ${passedChecks}/8 checks passed`); } } catch (error) { console.log(`โŒ Connection pooling test failed: ${error.message}`); } } // Test 2: Database Indexes function testDatabaseIndexes() { console.log('๐Ÿ“Š Testing database indexes...'); try { const indexesFile = path.join(MIGRATIONS_DIR, '012_add_performance_indexes.sql'); if (!fs.existsSync(indexesFile)) { console.log('โŒ Database indexes migration file not found'); return; } const content = fs.readFileSync(indexesFile, 'utf8'); const checks = [ { name: 'Users table indexes', pattern: /idx_users_/g, min: 2 }, { name: 'Documents table indexes', pattern: /idx_documents_/g, min: 8 }, { name: 'Processing jobs indexes', pattern: /idx_processing_jobs_/g, min: 5 }, { name: 'Composite indexes', pattern: /idx_.*_user_.*_created/g, min: 2 }, { name: 'Partial indexes', pattern: /WHERE deleted_at IS NULL/g, min: 1 }, { name: 'Index comments', pattern: /COMMENT ON INDEX/g, min: 3 }, { name: 'Performance indexes', pattern: /idx_.*_recent/g, min: 1 }, { name: 'Status-based indexes', pattern: /idx_.*_status/g, min: 3 }, ]; let passedChecks = 0; for (const check of checks) { const matches = content.match(check.pattern); if (matches && matches.length >= check.min) { passedChecks++; testResults.databaseIndexes.details.push(`${check.name}: ${matches.length} found`); } else { testResults.databaseIndexes.details.push(`${check.name}: ${matches?.length || 0} found (expected ${check.min}+)`); } } if (passedChecks >= 6) { testResults.databaseIndexes.passed = true; console.log(`โœ… Database indexes: ${passedChecks}/8 checks passed`); } else { console.log(`โŒ Database indexes: ${passedChecks}/8 checks passed`); } } catch (error) { console.log(`โŒ Database indexes test failed: ${error.message}`); } } // Test 3: Rate Limiting function testRateLimiting() { console.log('๐Ÿšฆ Testing rate limiting...'); try { const rateLimiterFile = path.join(BACKEND_DIR, 'middleware', 'rateLimiter.ts'); const content = fs.readFileSync(rateLimiterFile, 'utf8'); const checks = [ { name: 'Rate limit configurations', pattern: /RATE_LIMIT_CONFIGS/g, min: 1 }, { name: 'User rate limits', pattern: /USER_RATE_LIMITS/g, min: 1 }, { name: 'Rate limit store', pattern: /rateLimitStore/g, min: 1 }, { name: 'Cleanup mechanism', pattern: /cleanupExpiredLimits/g, min: 1 }, { name: 'User-specific limiters', pattern: /createUserRateLimiter/g, min: 1 }, { name: 'Rate limit headers', pattern: /X-RateLimit-/g, min: 3 }, { name: 'Subscription tiers', pattern: /free|basic|premium|enterprise/g, min: 4 }, { name: 'Rate limit monitoring', pattern: /getRateLimitStats/g, min: 1 }, ]; let passedChecks = 0; for (const check of checks) { const matches = content.match(check.pattern); if (matches && matches.length >= check.min) { passedChecks++; testResults.rateLimiting.details.push(`${check.name}: ${matches.length} found`); } else { testResults.rateLimiting.details.push(`${check.name}: ${matches?.length || 0} found (expected ${check.min}+)`); } } if (passedChecks >= 6) { testResults.rateLimiting.passed = true; console.log(`โœ… Rate limiting: ${passedChecks}/8 checks passed`); } else { console.log(`โŒ Rate limiting: ${passedChecks}/8 checks passed`); } } catch (error) { console.log(`โŒ Rate limiting test failed: ${error.message}`); } } // Test 4: Analytics Implementation function testAnalyticsImplementation() { console.log('๐Ÿ“ˆ Testing analytics implementation...'); try { const userModelFile = path.join(BACKEND_DIR, 'models', 'UserModel.ts'); const documentModelFile = path.join(BACKEND_DIR, 'models', 'DocumentModel.ts'); const userContent = fs.readFileSync(userModelFile, 'utf8'); const documentContent = fs.readFileSync(documentModelFile, 'utf8'); const checks = [ { name: 'User analytics - document count', pattern: /documentsProcessed: documents\.length/g, min: 1, content: userContent }, { name: 'User analytics - processing time', pattern: /totalProcessingTime = documents\.reduce/g, min: 1, content: userContent }, { name: 'User analytics - average time', pattern: /averageProcessingTime: Math\.round/g, min: 1, content: userContent }, { name: 'Document analytics - active users', pattern: /activeUsers = activeUsersError/g, min: 1, content: documentContent }, { name: 'Document analytics - processing time', pattern: /averageProcessingTime = processingError/g, min: 1, content: documentContent }, { name: 'Document analytics - cost tracking', pattern: /totalCost = costError/g, min: 1, content: documentContent }, { name: 'Analytics error handling', pattern: /catch \(error\)/g, min: 2, content: userContent + documentContent }, { name: 'Analytics logging', pattern: /logger\.error.*analytics/g, min: 2, content: userContent + documentContent }, ]; let passedChecks = 0; for (const check of checks) { const matches = check.content.match(check.pattern); if (matches && matches.length >= check.min) { passedChecks++; testResults.analyticsImplementation.details.push(`${check.name}: ${matches.length} found`); } else { testResults.analyticsImplementation.details.push(`${check.name}: ${matches?.length || 0} found (expected ${check.min}+)`); } } if (passedChecks >= 6) { testResults.analyticsImplementation.passed = true; console.log(`โœ… Analytics implementation: ${passedChecks}/8 checks passed`); } else { console.log(`โŒ Analytics implementation: ${passedChecks}/8 checks passed`); } } catch (error) { console.log(`โŒ Analytics implementation test failed: ${error.message}`); } } // Test 5: Integration with main application function testIntegration() { console.log('๐Ÿ”— Testing integration...'); try { const indexFile = path.join(BACKEND_DIR, 'index.ts'); const documentsRouteFile = path.join(BACKEND_DIR, 'routes', 'documents.ts'); const indexContent = fs.readFileSync(indexFile, 'utf8'); const documentsContent = fs.readFileSync(documentsRouteFile, 'utf8'); const checks = [ { name: 'Rate limiter imports', pattern: /import.*rateLimiter/g, min: 1, content: indexContent }, { name: 'Global rate limiter', pattern: /globalRateLimiter/g, min: 1, content: indexContent }, { name: 'Route-specific rate limiting', pattern: /uploadRateLimiter/g, min: 1, content: documentsContent }, { name: 'User rate limiting', pattern: /userUploadRateLimiter/g, min: 1, content: documentsContent }, { name: 'Processing rate limiting', pattern: /processingRateLimiter/g, min: 1, content: documentsContent }, ]; let passedChecks = 0; for (const check of checks) { const matches = check.content.match(check.pattern); if (matches && matches.length >= check.min) { passedChecks++; console.log(` โœ… ${check.name}: ${matches.length} found`); } else { console.log(` โŒ ${check.name}: ${matches?.length || 0} found (expected ${check.min}+)`); } } if (passedChecks >= 4) { console.log(`โœ… Integration: ${passedChecks}/5 checks passed`); } else { console.log(`โŒ Integration: ${passedChecks}/5 checks passed`); } } catch (error) { console.log(`โŒ Integration test failed: ${error.message}`); } } // Run all tests function runAllTests() { testConnectionPooling(); testDatabaseIndexes(); testRateLimiting(); testAnalyticsImplementation(); testIntegration(); // Calculate overall score const passedTests = Object.values(testResults).filter(result => result.passed && result !== testResults.overall).length; const totalTests = 4; testResults.overall.score = (passedTests / totalTests) * 100; testResults.overall.passed = passedTests >= 3; // At least 3 out of 4 tests must pass console.log('\n๐Ÿ“Š Phase 2 Test Results Summary:'); console.log('=================================='); console.log(`โœ… Connection Pooling: ${testResults.connectionPooling.passed ? 'PASSED' : 'FAILED'}`); console.log(`โœ… Database Indexes: ${testResults.databaseIndexes.passed ? 'PASSED' : 'FAILED'}`); console.log(`โœ… Rate Limiting: ${testResults.rateLimiting.passed ? 'PASSED' : 'FAILED'}`); console.log(`โœ… Analytics Implementation: ${testResults.analyticsImplementation.passed ? 'PASSED' : 'FAILED'}`); console.log(`\n๐ŸŽฏ Overall Score: ${testResults.overall.score.toFixed(1)}% (${passedTests}/${totalTests} tests passed)`); console.log(`๐Ÿ† Phase 2 Status: ${testResults.overall.passed ? 'COMPLETED' : 'NEEDS WORK'}`); // Save detailed results const resultsFile = path.join(__dirname, 'phase2-test-results.json'); fs.writeFileSync(resultsFile, JSON.stringify(testResults, null, 2)); console.log(`\n๐Ÿ“„ Detailed results saved to: ${resultsFile}`); return testResults.overall.passed; } // Run tests if this script is executed directly if (require.main === module) { const success = runAllTests(); process.exit(success ? 0 : 1); } module.exports = { runAllTests, testResults };