Files
drone-detector/server/tests/performance/load.test.js
2025-09-14 21:07:43 +02:00

479 lines
16 KiB
JavaScript

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,
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,
include: [{
model: models.DroneDetection,
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`);
});
});
});