← back to Designer Wallcoverings
DW-Agents/__tests__/performance/load-test.test.ts
279 lines
/**
* Performance and Load Tests
* Tests system performance under various load conditions
*/
import axios from 'axios';
const BASE_URL = process.env.BASE_URL || 'http://localhost';
const MASTER_HUB_PORT = process.env.MASTER_HUB_PORT || '9800';
const API_BASE = `${BASE_URL}:${MASTER_HUB_PORT}`;
describe('Performance Tests', () => {
describe('Response Time Tests', () => {
it('should respond to health check within 100ms under normal load', async () => {
const iterations = 10;
const times: number[] = [];
for (let i = 0; i < iterations; i++) {
const start = Date.now();
await axios.get(`${API_BASE}/api/health`);
times.push(Date.now() - start);
}
const avg = times.reduce((a, b) => a + b, 0) / times.length;
expect(avg).toBeLessThan(100);
});
it('should maintain consistent response times', async () => {
const iterations = 50;
const times: number[] = [];
for (let i = 0; i < iterations; i++) {
const start = Date.now();
await axios.get(`${API_BASE}/api/health`);
times.push(Date.now() - start);
await new Promise(resolve => setTimeout(resolve, 50));
}
const avg = times.reduce((a, b) => a + b, 0) / times.length;
const max = Math.max(...times);
const min = Math.min(...times);
// Standard deviation should be low (consistent performance)
const variance = times.reduce((sum, time) => sum + Math.pow(time - avg, 2), 0) / times.length;
const stdDev = Math.sqrt(variance);
expect(stdDev).toBeLessThan(avg); // Std dev should be less than average
expect(max).toBeLessThan(min * 5); // Max shouldn't be more than 5x min
});
});
describe('Throughput Tests', () => {
it('should handle 100 requests per second', async () => {
const duration = 5000; // 5 seconds
const targetRPS = 100;
const expectedRequests = (duration / 1000) * targetRPS;
const startTime = Date.now();
const promises: Promise<any>[] = [];
let completed = 0;
const interval = setInterval(() => {
for (let i = 0; i < 10; i++) { // Batch of 10
promises.push(
axios.get(`${API_BASE}/api/health`, { validateStatus: () => true })
.then(() => completed++)
.catch(() => completed++)
);
}
}, 100); // Every 100ms
// Wait for test duration
await new Promise(resolve => setTimeout(resolve, duration));
clearInterval(interval);
// Wait for all requests to complete
await Promise.all(promises);
const totalTime = Date.now() - startTime;
const actualRPS = (completed / totalTime) * 1000;
console.log(`Throughput test: ${actualRPS.toFixed(2)} req/s (target: ${targetRPS})`);
expect(completed).toBeGreaterThan(expectedRequests * 0.8); // At least 80% success
}, 10000);
it('should handle burst traffic', async () => {
// Send 200 requests as fast as possible
const burstSize = 200;
const startTime = Date.now();
const promises = Array(burstSize).fill(null).map(() =>
axios.get(`${API_BASE}/api/health`, { validateStatus: () => true })
);
const responses = await Promise.all(promises);
const duration = Date.now() - startTime;
const successful = responses.filter(r => r.status === 200).length;
const successRate = (successful / burstSize) * 100;
console.log(`Burst test: ${successful}/${burstSize} successful in ${duration}ms (${successRate.toFixed(1)}%)`);
expect(successRate).toBeGreaterThan(90); // 90% success rate
expect(duration).toBeLessThan(5000); // Complete within 5 seconds
}, 10000);
});
describe('Concurrency Tests', () => {
it('should handle 50 concurrent connections', async () => {
const concurrentRequests = 50;
const promises = Array(concurrentRequests).fill(null).map(() =>
axios.get(`${API_BASE}/api/health`, {
timeout: 5000,
validateStatus: () => true
})
);
const responses = await Promise.all(promises);
const successful = responses.filter(r => r.status === 200).length;
const successRate = (successful / concurrentRequests) * 100;
expect(successRate).toBeGreaterThan(95); // 95% success rate
});
it('should handle sustained concurrent load', async () => {
const duration = 10000; // 10 seconds
const concurrency = 20;
const startTime = Date.now();
const results: boolean[] = [];
const workers = Array(concurrency).fill(null).map(async () => {
while (Date.now() - startTime < duration) {
try {
const response = await axios.get(`${API_BASE}/api/health`, {
timeout: 2000
});
results.push(response.status === 200);
} catch {
results.push(false);
}
await new Promise(resolve => setTimeout(resolve, 100));
}
});
await Promise.all(workers);
const successRate = (results.filter(r => r).length / results.length) * 100;
console.log(`Sustained load test: ${successRate.toFixed(1)}% success over ${duration}ms`);
expect(successRate).toBeGreaterThan(95);
}, 15000);
});
describe('Stress Tests', () => {
it('should recover from overload', async () => {
// Phase 1: Overload (500 requests rapid fire)
const overloadPromises = Array(500).fill(null).map(() =>
axios.get(`${API_BASE}/api/health`, {
validateStatus: () => true,
timeout: 5000
}).catch(() => ({ status: 0 }))
);
await Promise.all(overloadPromises);
// Phase 2: Wait for recovery
await new Promise(resolve => setTimeout(resolve, 2000));
// Phase 3: Test normal operation
const recoveryPromises = Array(10).fill(null).map(() =>
axios.get(`${API_BASE}/api/health`)
);
const recoveryResponses = await Promise.all(recoveryPromises);
// Should fully recover
recoveryResponses.forEach(response => {
expect(response.status).toBe(200);
});
}, 20000);
it('should maintain performance under sustained load', async () => {
const testDuration = 30000; // 30 seconds
const requestInterval = 100; // 10 req/sec
const startTime = Date.now();
const responseTimes: number[] = [];
while (Date.now() - startTime < testDuration) {
const reqStart = Date.now();
try {
await axios.get(`${API_BASE}/api/health`, { timeout: 3000 });
responseTimes.push(Date.now() - reqStart);
} catch {
responseTimes.push(3000); // Timeout
}
await new Promise(resolve => setTimeout(resolve, requestInterval));
}
// Calculate performance degradation
const firstQuarter = responseTimes.slice(0, Math.floor(responseTimes.length / 4));
const lastQuarter = responseTimes.slice(-Math.floor(responseTimes.length / 4));
const firstAvg = firstQuarter.reduce((a, b) => a + b, 0) / firstQuarter.length;
const lastAvg = lastQuarter.reduce((a, b) => a + b, 0) / lastQuarter.length;
const degradation = ((lastAvg - firstAvg) / firstAvg) * 100;
console.log(`Performance degradation: ${degradation.toFixed(2)}%`);
// Performance shouldn't degrade more than 50%
expect(degradation).toBeLessThan(50);
}, 35000);
});
describe('Resource Utilization', () => {
it('should not leak memory under repeated requests', async () => {
// Make 1000 requests and track response consistency
const iterations = 1000;
let errors = 0;
for (let i = 0; i < iterations; i++) {
try {
const response = await axios.get(`${API_BASE}/api/health`, {
timeout: 2000
});
expect(response.status).toBe(200);
} catch {
errors++;
}
// Allow GC every 100 requests
if (i % 100 === 0) {
await new Promise(resolve => setTimeout(resolve, 100));
}
}
const errorRate = (errors / iterations) * 100;
expect(errorRate).toBeLessThan(5); // Less than 5% errors
}, 60000);
});
describe('Latency Distribution', () => {
it('should have acceptable p50, p95, and p99 latencies', async () => {
const iterations = 200;
const times: number[] = [];
for (let i = 0; i < iterations; i++) {
const start = Date.now();
await axios.get(`${API_BASE}/api/health`);
times.push(Date.now() - start);
await new Promise(resolve => setTimeout(resolve, 25));
}
times.sort((a, b) => a - b);
const p50 = times[Math.floor(iterations * 0.50)];
const p95 = times[Math.floor(iterations * 0.95)];
const p99 = times[Math.floor(iterations * 0.99)];
console.log(`Latency distribution:
P50: ${p50}ms
P95: ${p95}ms
P99: ${p99}ms
`);
expect(p50).toBeLessThan(100);
expect(p95).toBeLessThan(300);
expect(p99).toBeLessThan(500);
}, 15000);
});
});