const { describe, it, beforeEach, afterEach, before, after } = require('mocha'); const { expect } = require('chai'); const sinon = require('sinon'); const { setupTestEnvironment, teardownTestEnvironment, cleanDatabase, createTestUser, createTestTenant, createTestDevice, generateTestToken } = require('../setup'); describe('Performance Tests', () => { let models, sequelize; before(async () => { ({ models, sequelize } = await setupTestEnvironment()); }); after(async () => { await teardownTestEnvironment(); }); beforeEach(async () => { await cleanDatabase(); }); describe('Database Performance', () => { it('should handle large volume of detections efficiently', async function() { this.timeout(30000); // 30 second timeout for performance test const tenant = await createTestTenant(); const device = await createTestDevice({ tenant_id: tenant.id, is_approved: true }); const batchSize = 1000; const detections = []; // Prepare batch of detections for (let i = 0; i < batchSize; i++) { detections.push({ device_id: device.id, tenant_id: tenant.id, geo_lat: 59.3293 + (Math.random() * 0.01), geo_lon: 18.0686 + (Math.random() * 0.01), device_timestamp: new Date(Date.now() + (i * 1000)), drone_type: Math.floor(Math.random() * 19), rssi: -50 - Math.floor(Math.random() * 50), freq: 2400 + Math.floor(Math.random() * 100), drone_id: Math.floor(Math.random() * 10000), threat_level: ['low', 'medium', 'high', 'critical'][Math.floor(Math.random() * 4)], createdAt: new Date(), updatedAt: new Date() }); } const startTime = Date.now(); // Bulk insert detections await models.DroneDetection.bulkCreate(detections); const insertTime = Date.now() - startTime; // Test query performance const queryStartTime = Date.now(); const recentDetections = await models.DroneDetection.findAll({ where: { tenant_id: tenant.id, device_timestamp: { [models.Sequelize.Op.gte]: new Date(Date.now() - 3600000) // Last hour } }, order: [['device_timestamp', 'DESC']], limit: 100 }); const queryTime = Date.now() - queryStartTime; // Performance assertions expect(insertTime).to.be.lessThan(5000); // 5 seconds max for 1000 inserts expect(queryTime).to.be.lessThan(100); // 100ms max for query expect(recentDetections).to.have.length(100); console.log(`✅ Performance: ${batchSize} inserts in ${insertTime}ms, query in ${queryTime}ms`); }); it('should efficiently handle tenant-scoped queries with large datasets', async function() { this.timeout(30000); // Create multiple tenants with data const tenants = []; const devices = []; for (let t = 0; t < 5; t++) { const tenant = await createTestTenant({ slug: `perf-tenant-${t}` }); tenants.push(tenant); // Create devices for each tenant for (let d = 0; d < 10; d++) { const device = await createTestDevice({ id: (t * 100) + d, tenant_id: tenant.id, is_approved: true }); devices.push(device); } } // Create large dataset across all tenants const allDetections = []; devices.forEach(device => { for (let i = 0; i < 200; i++) { allDetections.push({ device_id: device.id, tenant_id: device.tenant_id, geo_lat: 59.3293 + (Math.random() * 0.1), geo_lon: 18.0686 + (Math.random() * 0.1), device_timestamp: new Date(Date.now() - (Math.random() * 86400000)), // Random within 24h drone_type: Math.floor(Math.random() * 19), rssi: -30 - Math.floor(Math.random() * 70), freq: 2400, drone_id: Math.floor(Math.random() * 50000), threat_level: ['low', 'medium', 'high', 'critical'][Math.floor(Math.random() * 4)], createdAt: new Date(), updatedAt: new Date() }); } }); await models.DroneDetection.bulkCreate(allDetections); // Test tenant isolation performance const testTenant = tenants[0]; const startTime = Date.now(); const tenantDetections = await models.DroneDetection.findAll({ where: { tenant_id: testTenant.id }, include: [{ model: models.Device, as: 'device', where: { tenant_id: testTenant.id } }], order: [['device_timestamp', 'DESC']], limit: 50 }); const queryTime = Date.now() - startTime; expect(queryTime).to.be.lessThan(200); // 200ms max with joins expect(tenantDetections).to.have.length(50); // Verify all results belong to correct tenant tenantDetections.forEach(detection => { expect(detection.tenant_id).to.equal(testTenant.id); expect(detection.Device.tenant_id).to.equal(testTenant.id); }); console.log(`✅ Tenant isolation query: ${queryTime}ms with ${allDetections.length} total records`); }); it('should handle concurrent user sessions efficiently', async function() { this.timeout(20000); const tenant = await createTestTenant(); const users = []; // Create multiple users for (let i = 0; i < 10; i++) { const user = await createTestUser({ username: `user${i}`, email: `user${i}@example.com`, tenant_id: tenant.id }); users.push(user); } // Simulate concurrent operations const concurrentOperations = users.map(async (user, index) => { const startTime = Date.now(); // Each user performs multiple operations const operations = [ // Create device models.Device.create({ id: 9000 + index, name: `Device ${index}`, tenant_id: tenant.id, geo_lat: 59.3293, geo_lon: 18.0686, is_approved: true }), // Query existing data models.DroneDetection.findAll({ where: { tenant_id: tenant.id }, limit: 10 }), // Create alert rule models.AlertRule.create({ tenant_id: tenant.id, name: `Rule ${index}`, is_active: true }) ]; await Promise.all(operations); return Date.now() - startTime; }); const operationTimes = await Promise.all(concurrentOperations); const averageTime = operationTimes.reduce((a, b) => a + b, 0) / operationTimes.length; expect(averageTime).to.be.lessThan(1000); // 1 second average console.log(`✅ Concurrent operations: ${operationTimes.length} users, ${averageTime.toFixed(2)}ms average`); }); }); describe('Memory Performance', () => { it('should efficiently manage memory with large tracking datasets', async () => { const DroneTrackingService = require('../../services/droneTrackingService'); const trackingService = new DroneTrackingService(); const initialMemory = process.memoryUsage().heapUsed; // Simulate tracking 1000 different drones for (let i = 0; i < 1000; i++) { const detection = { drone_id: i, geo_lat: 59.3293 + (Math.random() * 0.1), geo_lon: 18.0686 + (Math.random() * 0.1), device_timestamp: new Date(), rssi: -60, threat_level: 'medium' }; trackingService.trackDetection(detection); } const afterTrackingMemory = process.memoryUsage().heapUsed; const memoryIncrease = afterTrackingMemory - initialMemory; // Memory increase should be reasonable (less than 50MB for 1000 drones) expect(memoryIncrease).to.be.lessThan(50 * 1024 * 1024); // Test cleanup efficiency trackingService.cleanup(Date.now() + 1000); // Cleanup all const afterCleanupMemory = process.memoryUsage().heapUsed; const activeTracking = trackingService.getActiveTracking(); expect(activeTracking).to.have.length(0); console.log(`✅ Memory: +${(memoryIncrease / 1024 / 1024).toFixed(2)}MB for 1000 tracked drones`); }); it('should handle alert service memory efficiently', async () => { const AlertService = require('../../services/alertService'); const alertService = new AlertService(); const initialMemory = process.memoryUsage().heapUsed; // Generate many active alerts for (let i = 0; i < 500; i++) { const alertKey = `device_${i % 50}_drone_${i}`; alertService.activeAlerts.set(alertKey, { deviceId: i % 50, droneId: i, firstDetection: Date.now(), lastDetection: Date.now(), detectionCount: 1, threatLevel: 'high' }); } const afterAlertsMemory = process.memoryUsage().heapUsed; const memoryIncrease = afterAlertsMemory - initialMemory; // Memory should be reasonable expect(memoryIncrease).to.be.lessThan(20 * 1024 * 1024); // 20MB max // Test cleanup alertService.cleanupOldAlerts(Date.now() + 1000); expect(alertService.activeAlerts.size).to.equal(0); console.log(`✅ Alert memory: +${(memoryIncrease / 1024 / 1024).toFixed(2)}MB for 500 alerts`); }); }); describe('API Response Time Performance', () => { it('should maintain fast response times under load', async () => { const tenant = await createTestTenant(); const device = await createTestDevice({ tenant_id: tenant.id, is_approved: true }); // Create substantial detection history const detections = []; for (let i = 0; i < 2000; i++) { detections.push({ device_id: device.id, tenant_id: tenant.id, geo_lat: 59.3293 + (Math.random() * 0.01), geo_lon: 18.0686 + (Math.random() * 0.01), device_timestamp: new Date(Date.now() - (i * 60000)), // 1 minute intervals drone_type: Math.floor(Math.random() * 19), rssi: -50 - Math.floor(Math.random() * 50), freq: 2400, drone_id: Math.floor(Math.random() * 1000), threat_level: ['low', 'medium', 'high', 'critical'][Math.floor(Math.random() * 4)], createdAt: new Date(), updatedAt: new Date() }); } await models.DroneDetection.bulkCreate(detections); // Test various query patterns for response time const queries = [ { name: 'Recent detections', query: () => models.DroneDetection.findAll({ where: { tenant_id: tenant.id, device_timestamp: { [models.Sequelize.Op.gte]: new Date(Date.now() - 3600000) } }, order: [['device_timestamp', 'DESC']], limit: 50 }) }, { name: 'Device statistics', query: () => models.DroneDetection.findAll({ where: { tenant_id: tenant.id }, attributes: [ 'device_id', [models.sequelize.fn('COUNT', '*'), 'count'], [models.sequelize.fn('AVG', models.sequelize.col('rssi')), 'avg_rssi'] ], group: ['device_id'] }) }, { name: 'Threat analysis', query: () => models.DroneDetection.findAll({ where: { tenant_id: tenant.id, threat_level: ['high', 'critical'] }, order: [['device_timestamp', 'DESC']], limit: 100 }) } ]; for (const queryTest of queries) { const startTime = Date.now(); const result = await queryTest.query(); const queryTime = Date.now() - startTime; expect(queryTime).to.be.lessThan(500); // 500ms max expect(result).to.be.an('array'); console.log(`✅ ${queryTest.name}: ${queryTime}ms (${result.length} results)`); } }); it('should handle pagination efficiently', async () => { const tenant = await createTestTenant(); const device = await createTestDevice({ tenant_id: tenant.id }); // Create large dataset const detections = []; for (let i = 0; i < 5000; i++) { detections.push({ device_id: device.id, tenant_id: tenant.id, geo_lat: 59.3293, geo_lon: 18.0686, device_timestamp: new Date(Date.now() - (i * 1000)), drone_type: 2, rssi: -60, freq: 2400, drone_id: i, threat_level: 'medium', createdAt: new Date(), updatedAt: new Date() }); } await models.DroneDetection.bulkCreate(detections); // Test pagination performance at different offsets const pageSize = 50; const testPages = [0, 1000, 2000, 4000]; // Different offset positions for (const offset of testPages) { const startTime = Date.now(); const pageResults = await models.DroneDetection.findAndCountAll({ where: { tenant_id: tenant.id }, order: [['device_timestamp', 'DESC']], limit: pageSize, offset: offset }); const queryTime = Date.now() - startTime; expect(queryTime).to.be.lessThan(200); // 200ms max even for large offsets expect(pageResults.rows).to.have.length(pageSize); console.log(`✅ Page at offset ${offset}: ${queryTime}ms`); } }); }); describe('Scalability Tests', () => { it('should scale with increasing number of tenants', async function() { this.timeout(30000); const tenantCount = 20; const devicesPerTenant = 5; const detectionsPerDevice = 100; // Create multi-tenant environment for (let t = 0; t < tenantCount; t++) { const tenant = await createTestTenant({ slug: `scale-tenant-${t}` }); for (let d = 0; d < devicesPerTenant; d++) { const device = await createTestDevice({ id: (t * 1000) + d, tenant_id: tenant.id, is_approved: true }); // Create detections for this device const detections = []; for (let i = 0; i < detectionsPerDevice; i++) { detections.push({ device_id: device.id, tenant_id: tenant.id, geo_lat: 59.3293 + (Math.random() * 0.01), geo_lon: 18.0686 + (Math.random() * 0.01), device_timestamp: new Date(Date.now() - (Math.random() * 86400000)), drone_type: Math.floor(Math.random() * 19), rssi: -60 + Math.floor(Math.random() * 40), freq: 2400, drone_id: (t * 10000) + (d * 1000) + i, threat_level: ['low', 'medium', 'high'][Math.floor(Math.random() * 3)], createdAt: new Date(), updatedAt: new Date() }); } await models.DroneDetection.bulkCreate(detections); } } // Test query performance across all tenants const startTime = Date.now(); const allTenants = await models.Tenant.findAll({ include: [{ model: models.Device, as: 'devices', include: [{ model: models.DroneDetection, as: 'detections', limit: 10, order: [['device_timestamp', 'DESC']] }] }] }); const queryTime = Date.now() - startTime; expect(allTenants).to.have.length(tenantCount); expect(queryTime).to.be.lessThan(2000); // 2 seconds max for complex query console.log(`✅ Scalability: ${tenantCount} tenants, ${tenantCount * devicesPerTenant} devices, ${tenantCount * devicesPerTenant * detectionsPerDevice} detections in ${queryTime}ms`); }); }); });