Skip to main content
Glama
LoadTestScenarios.ts24.2 kB
/** * 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;

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Ghostseller/CastPlan_mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server