Files
roa2web-service-auto/reports-app/frontend/tests/integration/api-endpoints/health-check.spec.js
Marius Mutu 12ac2b671e feat: Frontend CSS refactoring and test improvements
Frontend:
- Refactored CSS architecture with new utility classes
- Updated dashboard components styling
- Improved responsive grid system
- Enhanced typography and variables
- Updated E2E and integration tests

Added:
- Claude Code slash commands for validation
- SSH tunnel and start test scripts

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-21 21:08:47 +02:00

407 lines
14 KiB
JavaScript

/**
* 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,
generateErrorReport,
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) => {
expect(metric.successCount).toBeGreaterThan(2); // At least 3/4 operations successful
});
console.log('✅ Backend resource monitoring validated - no significant degradation detected');
});
});