/**
* Load Test Scenarios for CastPlan Ultimate MCP Server
*
* Comprehensive load testing scenarios covering:
* - Stress Testing: Maximum capacity measurement
* - Volume Testing: Large document processing (500+ documents)
* - Endurance Testing: Long-running stability
* - Spike Testing: Sudden traffic increases
* - Concurrency Testing: Race conditions and deadlocks
*
* Created: 2025-01-30
*/
import {
AdvancedPerformanceFramework,
LoadTestConfig,
LoadTestScenario,
LoadTestOperation,
LoadPattern,
PerformanceThresholds
} from './AdvancedPerformanceFramework.js';
import { TestDataFactory } from '../helpers/TestUtils.js';
import { ServiceMockFactory } from '../helpers/MockFactories.js';
/**
* Load Test Scenarios Manager
*/
export class LoadTestScenarios {
private framework: AdvancedPerformanceFramework;
private services: any;
constructor(framework: AdvancedPerformanceFramework) {
this.framework = framework;
this.initializeServices();
}
/**
* Initialize mock services for testing
*/
private initializeServices(): void {
this.services = {
bmadService: ServiceMockFactory.createBMADServiceMock(),
documentationService: ServiceMockFactory.createDocumentationServiceMock(),
hooksService: ServiceMockFactory.createHooksServiceMock(),
dateTimeService: ServiceMockFactory.createDateTimeServiceMock(),
lifecycleService: ServiceMockFactory.createDocumentLifecycleServiceMock(),
connectionService: ServiceMockFactory.createWorkDocumentConnectionServiceMock(),
treeService: ServiceMockFactory.createDocumentTreeServiceMock(),
aiService: ServiceMockFactory.createAIAnalysisServiceMock()
};
}
/**
* Execute all load test scenarios
*/
async executeAllScenarios(): Promise<Map<string, any>> {
const results = new Map();
console.log('🚀 Starting comprehensive load test scenarios...\n');
try {
// 1. Stress Test - Maximum Capacity
console.log('📊 Executing Stress Test...');
const stressResult = await this.executeStressTest();
results.set('stress', stressResult);
console.log(`✅ Stress Test completed - Score: ${stressResult.summary.overallScore.toFixed(1)}/100\n`);
// 2. Volume Test - Large Document Processing
console.log('📚 Executing Volume Test...');
const volumeResult = await this.executeVolumeTest();
results.set('volume', volumeResult);
console.log(`✅ Volume Test completed - Score: ${volumeResult.summary.overallScore.toFixed(1)}/100\n`);
// 3. Endurance Test - Long-running Stability
console.log('⏰ Executing Endurance Test...');
const enduranceResult = await this.executeEnduranceTest();
results.set('endurance', enduranceResult);
console.log(`✅ Endurance Test completed - Score: ${enduranceResult.summary.overallScore.toFixed(1)}/100\n`);
// 4. Spike Test - Traffic Burst Handling
console.log('⚡ Executing Spike Test...');
const spikeResult = await this.executeSpikeTest();
results.set('spike', spikeResult);
console.log(`✅ Spike Test completed - Score: ${spikeResult.summary.overallScore.toFixed(1)}/100\n`);
// 5. Concurrency Test - Race Conditions and Deadlocks
console.log('🔄 Executing Concurrency Test...');
const concurrencyResult = await this.executeConcurrencyTest();
results.set('concurrency', concurrencyResult);
console.log(`✅ Concurrency Test completed - Score: ${concurrencyResult.summary.overallScore.toFixed(1)}/100\n`);
console.log('🎉 All load test scenarios completed successfully!');
return results;
} catch (error: any) {
console.error('❌ Load test scenarios failed:', error.message);
throw error;
}
}
/**
* Stress Test - Determine maximum system capacity
*/
async executeStressTest(): Promise<any> {
const config: LoadTestConfig = {
name: 'Stress Test - Maximum Capacity',
description: 'Gradually increase load until system breaks to find maximum capacity',
scenario: {
type: 'stress',
operations: this.createStressOperations(),
patterns: [
{
type: 'ramp',
parameters: {
startRPS: 10,
endRPS: 200,
steps: 20
}
}
]
},
duration: 300000, // 5 minutes
rampUpTime: 60000, // 1 minute ramp up
maxConcurrency: 100,
targetRPS: 150,
dataSet: this.generateLargeDataSet(1000),
warmupDuration: 30000,
cooldownDuration: 30000,
thresholds: {
maxResponseTime: 2000, // 2 seconds
maxMemoryUsage: 512 * 1024 * 1024, // 512MB
minThroughput: 100, // 100 ops/sec
maxErrorRate: 0.05, // 5%
maxCpuUtilization: 90, // 90%
maxDatabaseLatency: 500 // 500ms
}
};
return await this.framework.executeLoadTest(config);
}
/**
* Volume Test - Large document processing (500+ documents)
*/
async executeVolumeTest(): Promise<any> {
const config: LoadTestConfig = {
name: 'Volume Test - Large Document Processing',
description: 'Process 500+ documents simultaneously to test data handling capacity',
scenario: {
type: 'volume',
operations: this.createVolumeOperations(),
patterns: [
{
type: 'constant',
parameters: {
rps: 50
}
}
]
},
duration: 600000, // 10 minutes
rampUpTime: 30000,
maxConcurrency: 50,
targetRPS: 50,
dataSet: this.generateLargeDocumentSet(750), // 750 documents for thorough testing
warmupDuration: 15000,
cooldownDuration: 30000,
thresholds: {
maxResponseTime: 5000, // 5 seconds for large operations
maxMemoryUsage: 1024 * 1024 * 1024, // 1GB
minThroughput: 25, // 25 ops/sec minimum for large data
maxErrorRate: 0.02, // 2%
maxCpuUtilization: 85,
maxDatabaseLatency: 1000 // 1 second for complex queries
}
};
return await this.framework.executeLoadTest(config);
}
/**
* Endurance Test - Long-running stability
*/
async executeEnduranceTest(): Promise<any> {
const config: LoadTestConfig = {
name: 'Endurance Test - Long-running Stability',
description: 'Run steady load for extended period to test stability and memory leaks',
scenario: {
type: 'endurance',
operations: this.createEnduranceOperations(),
patterns: [
{
type: 'wave',
parameters: {
minRPS: 20,
maxRPS: 80,
waveLength: 120000 // 2 minute waves
}
}
]
},
duration: 1800000, // 30 minutes
rampUpTime: 60000,
maxConcurrency: 30,
targetRPS: 50,
dataSet: this.generateContinuousDataSet(2000),
warmupDuration: 30000,
cooldownDuration: 60000,
thresholds: {
maxResponseTime: 3000,
maxMemoryUsage: 768 * 1024 * 1024, // 768MB
minThroughput: 30,
maxErrorRate: 0.01, // 1% - strict for endurance
maxCpuUtilization: 75, // Lower for sustained load
maxDatabaseLatency: 750
}
};
return await this.framework.executeLoadTest(config);
}
/**
* Spike Test - Sudden traffic increases
*/
async executeSpikeTest(): Promise<any> {
const config: LoadTestConfig = {
name: 'Spike Test - Traffic Burst Handling',
description: 'Test system behavior under sudden traffic spikes',
scenario: {
type: 'spike',
operations: this.createSpikeOperations(),
patterns: [
{
type: 'spike',
parameters: {
baseRPS: 20,
spikeRPS: 150,
spikeDuration: 10000, // 10 second spikes
spikeInterval: 60000 // Every minute
}
}
]
},
duration: 420000, // 7 minutes
rampUpTime: 15000,
maxConcurrency: 150,
targetRPS: 50, // Average including spikes
dataSet: this.generateSpikeDataSet(800),
warmupDuration: 15000,
cooldownDuration: 30000,
thresholds: {
maxResponseTime: 8000, // Allow higher during spikes
maxMemoryUsage: 640 * 1024 * 1024, // 640MB
minThroughput: 15, // Lower minimum due to base load
maxErrorRate: 0.08, // 8% - spikes may cause temporary issues
maxCpuUtilization: 95, // Allow higher during spikes
maxDatabaseLatency: 2000 // 2 seconds during spikes
}
};
return await this.framework.executeLoadTest(config);
}
/**
* Concurrency Test - Race conditions and deadlocks
*/
async executeConcurrencyTest(): Promise<any> {
const config: LoadTestConfig = {
name: 'Concurrency Test - Race Conditions and Deadlocks',
description: 'Test concurrent operations for race conditions, deadlocks, and data consistency',
scenario: {
type: 'concurrency',
operations: this.createConcurrencyOperations(),
patterns: [
{
type: 'burst',
parameters: {
burstSize: 75, // 75 simultaneous operations
burstInterval: 15000, // Every 15 seconds
restPeriod: 5000 // 5 second rest
}
}
]
},
duration: 480000, // 8 minutes
rampUpTime: 30000,
maxConcurrency: 75,
targetRPS: 40,
dataSet: this.generateConcurrencyDataSet(500),
warmupDuration: 20000,
cooldownDuration: 30000,
thresholds: {
maxResponseTime: 4000,
maxMemoryUsage: 512 * 1024 * 1024,
minThroughput: 20,
maxErrorRate: 0.03, // 3% - some conflicts expected
maxCpuUtilization: 80,
maxDatabaseLatency: 1500
}
};
return await this.framework.executeLoadTest(config);
}
/**
* Create stress test operations
*/
private createStressOperations(): LoadTestOperation[] {
return [
{
name: 'bmad_parse_specification',
weight: 25,
operation: async () => {
return await this.services.bmadService.parseSpecification({
content: this.generateRandomMarkdown(),
format: 'markdown'
});
},
validation: (result) => result && result.tasks && Array.isArray(result.tasks),
timeout: 5000
},
{
name: 'documentation_process_request',
weight: 30,
operation: async () => {
return await this.services.documentationService.processDocumentationRequest({
action: 'reference',
files: this.generateRandomFilePaths(3),
context: this.generateRandomContext()
});
},
validation: (result) => result && result.success,
timeout: 4000
},
{
name: 'hooks_process_event',
weight: 20,
operation: async () => {
return await this.services.hooksService.processHookRequest({
event: {
type: 'file-change',
data: { filePath: this.generateRandomFilePath(), changeType: 'modified' },
timestamp: new Date().toISOString()
}
});
},
validation: (result) => result && result.processed,
timeout: 3000
},
{
name: 'lifecycle_create_document',
weight: 15,
operation: async () => {
return await this.services.lifecycleService.createDocument(
TestDataFactory.createMockDocument({
title: `Stress Document ${Date.now()}`,
filePath: this.generateRandomFilePath()
})
);
},
validation: (result) => result && result.id,
timeout: 3000
},
{
name: 'ai_analyze_quality',
weight: 10,
operation: async () => {
return await this.services.aiService.analyzeQuality(this.generateRandomFilePath());
},
validation: (result) => result && typeof result.score === 'number',
timeout: 8000
}
];
}
/**
* Create volume test operations focused on large data processing
*/
private createVolumeOperations(): LoadTestOperation[] {
return [
{
name: 'bulk_document_creation',
weight: 40,
operation: async () => {
const documents = Array.from({ length: 10 }, (_, i) =>
TestDataFactory.createMockDocument({
title: `Volume Document ${Date.now()}-${i}`,
filePath: `/docs/volume/doc${Date.now()}-${i}.md`
})
);
return await Promise.all(
documents.map(doc => this.services.lifecycleService.createDocument(doc))
);
},
validation: (results) => Array.isArray(results) && results.length === 10,
timeout: 15000
},
{
name: 'large_tree_construction',
weight: 25,
operation: async () => {
const documents = Array.from({ length: 50 }, (_, i) =>
TestDataFactory.createMockDocument({ id: `tree-doc-${Date.now()}-${i}` })
);
return await this.services.treeService.buildTree(documents);
},
validation: (result) => result && result.nodes && result.nodes.length > 0,
timeout: 20000
},
{
name: 'bulk_connection_creation',
weight: 20,
operation: async () => {
const connections = Array.from({ length: 15 }, (_, i) =>
TestDataFactory.createMockConnection({
workDescription: `Volume connection ${Date.now()}-${i}`,
filePaths: this.generateRandomFilePaths(5)
})
);
return await Promise.all(
connections.map(conn => this.services.connectionService.createConnection(conn))
);
},
validation: (results) => Array.isArray(results) && results.length === 15,
timeout: 12000
},
{
name: 'massive_documentation_search',
weight: 15,
operation: async () => {
const searchTerms = ['component', 'service', 'interface', 'model', 'utility'];
return await Promise.all(
searchTerms.map(term => this.services.documentationService.searchDocumentation(term))
);
},
validation: (results) => Array.isArray(results) && results.length === 5,
timeout: 10000
}
];
}
/**
* Create endurance test operations for long-running stability
*/
private createEnduranceOperations(): LoadTestOperation[] {
return [
{
name: 'sustained_bmad_operations',
weight: 35,
operation: async () => {
return await this.services.bmadService.parseSpecification({
content: this.generateRandomMarkdown(500), // Larger content
format: 'markdown'
});
},
validation: (result) => result && result.tasks,
timeout: 6000
},
{
name: 'continuous_document_updates',
weight: 30,
operation: async () => {
const docId = `endurance-doc-${Math.floor(Math.random() * 100)}`;
const states = ['draft', 'review', 'approved', 'published'];
const newState = states[Math.floor(Math.random() * states.length)];
return await this.services.lifecycleService.updateDocumentState(docId, newState);
},
validation: (result) => result && result.state,
timeout: 4000
},
{
name: 'persistent_hook_monitoring',
weight: 20,
operation: async () => {
return await this.services.hooksService.processHookRequest({
event: {
type: 'directory-change',
data: {
directoryPath: `/src/components/feature-${Math.floor(Math.random() * 50)}`,
changeType: 'modified'
},
timestamp: new Date().toISOString()
}
});
},
validation: (result) => result && result.processed,
timeout: 3000
},
{
name: 'memory_intensive_operations',
weight: 15,
operation: async () => {
// Simulate memory-intensive operations
const largeArray = new Array(10000).fill(0).map((_, i) => ({
id: i,
data: `Large data chunk ${i}`,
timestamp: Date.now(),
metadata: { processed: false, priority: Math.random() }
}));
// Process the array
return largeArray.filter(item => item.metadata.priority > 0.5).length;
},
validation: (result) => typeof result === 'number' && result >= 0,
timeout: 5000
}
];
}
/**
* Create spike test operations
*/
private createSpikeOperations(): LoadTestOperation[] {
return [
{
name: 'rapid_task_creation',
weight: 40,
operation: async () => {
return await this.services.bmadService.parseSpecification({
content: `# Spike Task ${Date.now()}\n\n- [ ] Handle traffic spike\n- [ ] Maintain performance`,
format: 'markdown'
});
},
validation: (result) => result && result.tasks,
timeout: 3000
},
{
name: 'concurrent_document_access',
weight: 35,
operation: async () => {
const docId = `spike-doc-${Math.floor(Math.random() * 20)}`;
return await this.services.lifecycleService.getDocument(docId);
},
validation: (result) => result !== null,
timeout: 2000
},
{
name: 'burst_hook_events',
weight: 25,
operation: async () => {
const events = Array.from({ length: 5 }, (_, i) => ({
type: 'file-change',
data: { filePath: `/tmp/spike-${Date.now()}-${i}.ts`, changeType: 'created' },
timestamp: new Date().toISOString()
}));
return await Promise.all(
events.map(event => this.services.hooksService.processHookRequest({ event }))
);
},
validation: (results) => Array.isArray(results) && results.length === 5,
timeout: 4000
}
];
}
/**
* Create concurrency test operations
*/
private createConcurrencyOperations(): LoadTestOperation[] {
return [
{
name: 'concurrent_task_status_updates',
weight: 30,
operation: async () => {
const taskId = `concurrent-task-${Math.floor(Math.random() * 10)}`;
const statuses = ['pending', 'in-progress', 'completed', 'blocked'];
const newStatus = statuses[Math.floor(Math.random() * statuses.length)];
return await this.services.bmadService.updateTaskStatus(taskId, newStatus);
},
validation: (result) => result && result.status,
timeout: 3000
},
{
name: 'concurrent_document_state_changes',
weight: 25,
operation: async () => {
const docId = `concurrent-doc-${Math.floor(Math.random() * 15)}`;
const states = ['draft', 'review', 'approved', 'published', 'archived'];
const newState = states[Math.floor(Math.random() * states.length)];
return await this.services.lifecycleService.updateDocumentState(docId, newState);
},
validation: (result) => result && result.state,
timeout: 4000
},
{
name: 'concurrent_connection_strength_updates',
weight: 20,
operation: async () => {
const connectionId = `concurrent-connection-${Math.floor(Math.random() * 12)}`;
const strength = Math.random();
return await this.services.connectionService.updateConnectionStrength(connectionId, strength);
},
validation: (result) => result && typeof result.strength === 'number',
timeout: 3000
},
{
name: 'concurrent_tree_modifications',
weight: 15,
operation: async () => {
const nodeId = `concurrent-node-${Math.floor(Math.random() * 8)}`;
const newPosition = Math.floor(Math.random() * 100);
return await this.services.treeService.updateNodePosition(nodeId, newPosition);
},
validation: (result) => result && typeof result.position === 'number',
timeout: 3000
},
{
name: 'concurrent_ai_analyses',
weight: 10,
operation: async () => {
const filePath = `/concurrent/analysis-${Math.floor(Math.random() * 5)}.md`;
return await this.services.aiService.analyzeQuality(filePath);
},
validation: (result) => result && typeof result.score === 'number',
timeout: 8000
}
];
}
// Data generation methods
private generateLargeDataSet(size: number): any[] {
return Array.from({ length: size }, (_, i) => ({
id: i,
type: 'test-data',
content: `Test data item ${i} with some content`,
timestamp: Date.now(),
metadata: { category: `category-${i % 10}` }
}));
}
private generateLargeDocumentSet(size: number): any[] {
return Array.from({ length: size }, (_, i) =>
TestDataFactory.createMockDocument({
id: `volume-doc-${i}`,
title: `Volume Test Document ${i}`,
filePath: `/docs/volume/document-${i}.md`,
category: `category-${i % 5}`
})
);
}
private generateContinuousDataSet(size: number): any[] {
return Array.from({ length: size }, (_, i) => ({
id: `continuous-${i}`,
data: `Continuous test data ${i}`,
priority: Math.random(),
timestamp: Date.now() + (i * 1000) // Spread over time
}));
}
private generateSpikeDataSet(size: number): any[] {
return Array.from({ length: size }, (_, i) => ({
id: `spike-${i}`,
urgency: i < size * 0.3 ? 'high' : 'normal', // 30% high urgency
payload: `Spike test payload ${i}`,
timestamp: Date.now()
}));
}
private generateConcurrencyDataSet(size: number): any[] {
return Array.from({ length: size }, (_, i) => ({
id: `concurrency-${i}`,
resourceId: `resource-${i % 20}`, // Shared resources for contention
operation: ['read', 'write', 'update', 'delete'][i % 4],
timestamp: Date.now()
}));
}
private generateRandomMarkdown(lines: number = 10): string {
const sections = ['Introduction', 'Requirements', 'Implementation', 'Testing', 'Deployment'];
const tasks = ['Implement feature', 'Write tests', 'Update documentation', 'Code review', 'Deploy to production'];
let content = `# Test Document ${Date.now()}\n\n`;
for (let i = 0; i < Math.min(lines / 3, sections.length); i++) {
content += `## ${sections[i]}\n\n`;
content += `This section covers ${sections[i].toLowerCase()}.\n\n`;
// Add some tasks
for (let j = 0; j < Math.min(3, tasks.length); j++) {
content += `- [ ] ${tasks[j]} for ${sections[i].toLowerCase()}\n`;
}
content += '\n';
}
return content;
}
private generateRandomContext(): string {
const contexts = [
'Frontend component development',
'Backend API implementation',
'Database schema updates',
'Testing automation',
'Documentation updates',
'Performance optimization',
'Security enhancements',
'User interface improvements'
];
return contexts[Math.floor(Math.random() * contexts.length)];
}
private generateRandomFilePath(): string {
const dirs = ['src', 'test', 'docs', 'config', 'scripts'];
const files = ['component.tsx', 'service.ts', 'model.ts', 'util.ts', 'config.json', 'README.md'];
const dir = dirs[Math.floor(Math.random() * dirs.length)];
const file = files[Math.floor(Math.random() * files.length)];
return `/${dir}/${file}`;
}
private generateRandomFilePaths(count: number): string[] {
return Array.from({ length: count }, () => this.generateRandomFilePath());
}
}
export default LoadTestScenarios;