/** * Backend Health Monitoring Integration Tests * Validates backend services, database connectivity, and error handling * Monitors system health through comprehensive endpoint testing */ import { test, expect } from '@playwright/test'; import { API_ENDPOINTS } from '../../utils/real-auth.js'; import { setupConsoleCapture, assertNoCriticalErrors, generateErrorReport, PerformanceBaselines, assertPerformanceBaseline } from '../../utils/console-monitor.js'; test.describe('Backend Health Monitoring', () => { test.beforeEach(async ({ page }) => { setupConsoleCapture(page); }); test.afterEach(async ({ page }) => { const report = generateErrorReport(page, test.info().title); if (report.summary.classifications.critical > 0) { console.warn('⚠️ Critical errors in health monitoring:', report.details.criticalErrors); } }); test('should validate database connectivity through health endpoint', async ({ page }) => { console.log('🏥 Testing backend health endpoint...'); const healthStartTime = Date.now(); const response = await page.request.get(`${API_ENDPOINTS.backend}${API_ENDPOINTS.health}`); const healthResponseTime = Date.now() - healthStartTime; // Validate response expect(response.status()).toBe(200); const health = await response.json(); console.log('📊 Health response:', health); // Validate health data structure expect(health).toHaveProperty('database'); expect(health).toHaveProperty('api'); expect(health).toHaveProperty('timestamp'); // Database should be connected (Oracle via SSH tunnel) expect(health.database).toBe('connected'); expect(health.api).toBe('healthy'); // Validate response time assertPerformanceBaseline( healthResponseTime, 1000, // Max 1s for health check 'Health endpoint response', expect ); console.log(`✅ Backend health validated in ${healthResponseTime}ms`); }); test('should validate SSH tunnel dependency in health check', async ({ page }) => { console.log('🔐 Testing SSH tunnel dependency validation...'); const response = await page.request.get(`${API_ENDPOINTS.backend}${API_ENDPOINTS.health}`); expect(response.status()).toBe(200); const health = await response.json(); // Should indicate SSH tunnel status if (health.ssh_tunnel !== undefined) { expect(health.ssh_tunnel).toBe('active'); console.log('✅ SSH tunnel status confirmed in health check'); } // Database connection implies SSH tunnel is working expect(health.database).toBe('connected'); console.log('✅ SSH tunnel dependency validated through database connectivity'); }); test('should handle Oracle connection failures gracefully', async ({ page }) => { console.log('💥 Testing Oracle connection failure handling...'); // First verify normal operation const normalResponse = await page.request.get(`${API_ENDPOINTS.backend}${API_ENDPOINTS.health}`); expect(normalResponse.status()).toBe(200); // Navigate to app to test error handling in UI await page.goto('/dashboard'); // Mock database connection failure await page.route('**/api/**', async (route) => { if (route.request().url().includes('/health')) { await route.fulfill({ status: 503, contentType: 'application/json', body: JSON.stringify({ database: 'disconnected', api: 'degraded', error: 'Oracle connection failed' }) }); } else { await route.continue(); } }); // Trigger health check from frontend await page.reload(); // Should show appropriate error handling const errorElements = await page.locator('[data-testid*="error"], [data-testid*="warning"]').count(); if (errorElements > 0) { console.log(`🚨 Found ${errorElements} error indicators in UI`); } // Check console for appropriate error messages (not critical failures) const consoleMessages = page.consoleMessages || []; const dbErrors = consoleMessages.filter(msg => msg.text.includes('database') || msg.text.includes('Oracle') || msg.text.includes('503') ); expect(dbErrors.length).toBeGreaterThan(0); console.log(`📝 Found ${dbErrors.length} database-related console messages`); // Should not crash the application const currentUrl = page.url(); expect(currentUrl).not.toContain('/error'); console.log('✅ Oracle connection failure handled gracefully'); }); test('should validate API endpoint availability and response times', async ({ page }) => { console.log('📡 Testing API endpoint availability...'); const endpoints = [ { path: API_ENDPOINTS.health, name: 'Health Check', maxTime: 1000 }, { path: API_ENDPOINTS.companies, name: 'Companies', maxTime: 2000 }, { path: `${API_ENDPOINTS.invoices}/ROMFAST`, name: 'ROMFAST Invoices', maxTime: 3000 }, { path: `${API_ENDPOINTS.payments}/ROMFAST`, name: 'ROMFAST Payments', maxTime: 3000 } ]; const results = []; for (const endpoint of endpoints) { console.log(`🔍 Testing ${endpoint.name} endpoint...`); const startTime = Date.now(); try { const response = await page.request.get(`${API_ENDPOINTS.backend}${endpoint.path}`); const responseTime = Date.now() - startTime; const result = { name: endpoint.name, path: endpoint.path, status: response.status(), responseTime, success: response.ok(), maxTime: endpoint.maxTime }; results.push(result); if (response.ok()) { console.log(`✅ ${endpoint.name}: ${result.status} (${responseTime}ms)`); // Validate response time assertPerformanceBaseline( responseTime, endpoint.maxTime, `${endpoint.name} response time`, expect ); } else { console.warn(`⚠️ ${endpoint.name}: ${result.status} (${responseTime}ms)`); } } catch (error) { console.error(`❌ ${endpoint.name} failed:`, error.message); results.push({ name: endpoint.name, path: endpoint.path, error: error.message, success: false }); } } // Summary const successCount = results.filter(r => r.success).length; const totalEndpoints = endpoints.length; console.log(`📊 API Endpoint Summary: ${successCount}/${totalEndpoints} successful`); // At least health and companies endpoints should work const criticalEndpoints = results.filter(r => r.name === 'Health Check' || r.name === 'Companies' ); const criticalSuccess = criticalEndpoints.filter(r => r.success).length; expect(criticalSuccess).toBe(2); // Both critical endpoints must work console.log('✅ Critical API endpoints validated'); }); test('should monitor backend error rates and patterns', async ({ page }) => { console.log('📈 Monitoring backend error rates...'); const testRequests = [ { url: `${API_ENDPOINTS.backend}${API_ENDPOINTS.health}`, expected: 200 }, { url: `${API_ENDPOINTS.backend}${API_ENDPOINTS.companies}`, expected: 200 }, { url: `${API_ENDPOINTS.backend}/api/nonexistent`, expected: 404 }, { url: `${API_ENDPOINTS.backend}/api/invoices/INVALID_COMPANY`, expected: 404 } ]; const errorPatterns = {}; let totalRequests = 0; let errorCount = 0; for (const request of testRequests) { totalRequests++; console.log(`📤 Testing: ${request.url}`); try { const response = await page.request.get(request.url); const status = response.status(); if (status !== request.expected) { errorCount++; const pattern = `${Math.floor(status / 100)}xx`; errorPatterns[pattern] = (errorPatterns[pattern] || 0) + 1; console.warn(`⚠️ Unexpected status: ${status} (expected ${request.expected})`); } else { console.log(`✅ Expected status: ${status}`); } } catch (error) { errorCount++; errorPatterns['network'] = (errorPatterns['network'] || 0) + 1; console.error(`❌ Network error:`, error.message); } } const errorRate = (errorCount / totalRequests) * 100; console.log(`📊 Error Analysis:`); console.log(` Total Requests: ${totalRequests}`); console.log(` Errors: ${errorCount}`); console.log(` Error Rate: ${errorRate.toFixed(1)}%`); console.log(` Error Patterns:`, errorPatterns); // Error rate should be reasonable (some 404s expected) expect(errorRate).toBeLessThan(75); // Allow some expected errors // Should not have network errors expect(errorPatterns.network || 0).toBe(0); console.log('✅ Backend error monitoring completed'); }); test('should validate backend performance under concurrent requests', async ({ page }) => { console.log('⚡ Testing backend performance under load...'); const concurrentRequests = 5; const requestsPerBatch = 3; const totalBatches = concurrentRequests; const performanceResults = []; for (let batch = 0; batch < totalBatches; batch++) { console.log(`🔄 Batch ${batch + 1}/${totalBatches}`); const batchStartTime = Date.now(); const batchPromises = []; // Create concurrent requests for (let req = 0; req < requestsPerBatch; req++) { const requestPromise = page.request.get(`${API_ENDPOINTS.backend}${API_ENDPOINTS.health}`) .then(response => ({ status: response.status(), timing: Date.now() - batchStartTime })) .catch(error => ({ error: error.message, timing: Date.now() - batchStartTime })); batchPromises.push(requestPromise); } // Wait for all requests in batch const batchResults = await Promise.all(batchPromises); const batchTime = Date.now() - batchStartTime; performanceResults.push({ batch: batch + 1, results: batchResults, totalTime: batchTime, successful: batchResults.filter(r => r.status === 200).length }); console.log(`⏱️ Batch ${batch + 1}: ${batchTime}ms (${batchResults.filter(r => r.status === 200).length}/${requestsPerBatch} successful)`); // Small delay between batches await new Promise(resolve => setTimeout(resolve, 100)); } // Analyze results const totalRequests = totalBatches * requestsPerBatch; const successfulRequests = performanceResults.reduce((sum, batch) => sum + batch.successful, 0); const averageBatchTime = performanceResults.reduce((sum, batch) => sum + batch.totalTime, 0) / totalBatches; const successRate = (successfulRequests / totalRequests) * 100; console.log(`📊 Concurrent Load Test Results:`); console.log(` Total Requests: ${totalRequests}`); console.log(` Successful: ${successfulRequests}`); console.log(` Success Rate: ${successRate.toFixed(1)}%`); console.log(` Average Batch Time: ${averageBatchTime.toFixed(0)}ms`); // Validate performance under load expect(successRate).toBeGreaterThan(90); // 90%+ success rate expect(averageBatchTime).toBeLessThan(5000); // Max 5s per batch // Individual requests should not be extremely slow const slowRequests = performanceResults .flatMap(batch => batch.results) .filter(result => result.timing > 10000); // > 10s expect(slowRequests.length).toBe(0); console.log('✅ Backend performance under concurrent load validated'); }); test('should validate backend memory and resource monitoring', async ({ page }) => { console.log('💾 Testing backend resource monitoring...'); // Test multiple heavy operations to check for memory leaks const operations = [ () => page.request.get(`${API_ENDPOINTS.backend}${API_ENDPOINTS.companies}`), () => page.request.get(`${API_ENDPOINTS.backend}/api/invoices/ROMFAST`), () => page.request.get(`${API_ENDPOINTS.backend}/api/payments/ROMFAST`), () => page.request.get(`${API_ENDPOINTS.backend}${API_ENDPOINTS.health}`) ]; const resourceMetrics = []; // Perform operations multiple times for (let cycle = 0; cycle < 3; cycle++) { console.log(`🔄 Resource test cycle ${cycle + 1}/3`); const cycleStartTime = Date.now(); const cycleResults = []; for (const operation of operations) { const opStartTime = Date.now(); try { const response = await operation(); const responseTime = Date.now() - opStartTime; cycleResults.push({ success: response.ok(), status: response.status(), responseTime }); } catch (error) { cycleResults.push({ success: false, error: error.message, responseTime: Date.now() - opStartTime }); } } const cycleTime = Date.now() - cycleStartTime; const avgResponseTime = cycleResults.reduce((sum, r) => sum + r.responseTime, 0) / cycleResults.length; resourceMetrics.push({ cycle: cycle + 1, totalTime: cycleTime, averageResponseTime: avgResponseTime, successCount: cycleResults.filter(r => r.success).length }); console.log(`📊 Cycle ${cycle + 1}: ${cycleTime}ms avg, ${avgResponseTime.toFixed(0)}ms response`); } // Check for performance degradation over cycles (indicating resource leaks) const firstCycleAvg = resourceMetrics[0].averageResponseTime; const lastCycleAvg = resourceMetrics[resourceMetrics.length - 1].averageResponseTime; const degradationRatio = lastCycleAvg / firstCycleAvg; console.log(`📈 Performance degradation ratio: ${degradationRatio.toFixed(2)}`); // Should not degrade significantly (< 50% increase) expect(degradationRatio).toBeLessThan(1.5); // All cycles should maintain good success rates resourceMetrics.forEach((metric, index) => { expect(metric.successCount).toBeGreaterThan(2); // At least 3/4 operations successful }); console.log('✅ Backend resource monitoring validated - no significant degradation detected'); }); });