Fix jwt-token
This commit is contained in:
478
server/tests/performance/load.test.js
Normal file
478
server/tests/performance/load.test.js
Normal file
@@ -0,0 +1,478 @@
|
||||
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`);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user