Skip to main content
Glama
integration-test.ts11.9 kB
/** * Integration Test Suite * Validates system stability and performance under various load conditions */ import { EventEmitter } from 'events'; interface IntegrationTestResult { name: string; passed: boolean; duration: number; metrics: Record<string, any>; errors: string[]; } class IntegrationTestSuite { private results: IntegrationTestResult[] = []; private isRunning = true; /** * Test 1: 24-Hour Stability Test * Simulates long-running server with periodic activity */ async testStability24Hour(): Promise<void> { console.log('\n🔄 Integration Test 1: 24-Hour Stability Simulation'); console.log('─'.repeat(60)); const startTime = Date.now(); const testDuration = 30000; // 30 seconds simulating 24 hours const scaleFactor = 1440 * 60 / 30; // Compress 24h into 30s let iterationCount = 0; let errorCount = 0; let memoryPeaks: number[] = []; console.log('Starting stability test (30s simulated 24 hours)...'); const memInitial = process.memoryUsage().heapUsed; while (Date.now() - startTime < testDuration) { iterationCount++; try { // Simulate various operations const cache = new Map(); for (let i = 0; i < 100; i++) { cache.set(`key_${i}`, { data: Math.random() }); } // Simulate WebSocket activity const subscriptions = new Map(); for (let i = 0; i < 50; i++) { subscriptions.set(i, () => {}); subscriptions.delete(i); } // Simulate SSE broadcasts for (let i = 0; i < 100; i++) { JSON.stringify({ event: 'state_changed', data: {} }); } // Check memory periodically if (iterationCount % 100 === 0) { const currentMemory = process.memoryUsage().heapUsed; memoryPeaks.push(currentMemory); const delta = currentMemory - memInitial; console.log(` Iteration ${iterationCount}: Memory delta = ${(delta / 1024 / 1024).toFixed(2)} MB`); // Memory should not increase significantly (< 50MB growth) if (Math.abs(delta) > 50 * 1024 * 1024) { throw new Error(`Memory spike detected: ${(delta / 1024 / 1024).toFixed(2)} MB`); } } } catch (error) { errorCount++; console.error(` Error at iteration ${iterationCount}:`, error); } } const duration = Date.now() - startTime; const memFinal = process.memoryUsage().heapUsed; this.results.push({ name: 'Stability 24-Hour', passed: errorCount === 0, duration, metrics: { iterations: iterationCount, errors: errorCount, memoryDeltaMB: (memFinal - memInitial) / 1024 / 1024, memoryPeakMB: Math.max(...memoryPeaks) / 1024 / 1024, avgIterationTime: duration / iterationCount }, errors: errorCount > 0 ? [`${errorCount} errors occurred during stability test`] : [] }); console.log(`✓ Test duration: ${duration}ms`); console.log(`✓ Iterations: ${iterationCount}`); console.log(`✓ Errors: ${errorCount}`); console.log(`✓ Memory delta: ${((memFinal - memInitial) / 1024 / 1024).toFixed(2)} MB`); } /** * Test 2: Load Test with 1000+ Clients * Simulates multiple concurrent SSE connections */ async testLoadWith1000Clients(): Promise<void> { console.log('\n🔄 Integration Test 2: Load Test with 1000+ Clients'); console.log('─'.repeat(60)); const NUM_CLIENTS = 1000; const BROADCAST_COUNT = 1000; const startTime = Date.now(); const clients = new Map<string, EventEmitter>(); let broadcastsSucceeded = 0; let broadcastsFailed = 0; console.log(`Creating ${NUM_CLIENTS} simulated SSE clients...`); // Create clients for (let i = 0; i < NUM_CLIENTS; i++) { const clientId = `client_${i}`; const client = new EventEmitter(); clients.set(clientId, client); } console.log(`Broadcasting ${BROADCAST_COUNT} messages to ${NUM_CLIENTS} clients...`); // Simulate broadcasts for (let broadcast = 0; broadcast < BROADCAST_COUNT; broadcast++) { const message = { id: broadcast, timestamp: new Date().toISOString(), data: { entityId: 'light.test', state: 'on' } }; const serialized = JSON.stringify(message); try { for (const [clientId, client] of clients.entries()) { client.emit('message', serialized); } broadcastsSucceeded++; } catch (error) { broadcastsFailed++; } if ((broadcast + 1) % 100 === 0) { console.log(` Broadcasts: ${broadcast + 1}/${BROADCAST_COUNT}`); } } const duration = Date.now() - startTime; this.results.push({ name: 'Load Test 1000+ Clients', passed: broadcastsFailed === 0, duration, metrics: { totalClients: NUM_CLIENTS, totalBroadcasts: BROADCAST_COUNT, totalMessages: NUM_CLIENTS * BROADCAST_COUNT, broadcastsSucceeded, broadcastsFailed, messagesPerSecond: (NUM_CLIENTS * BROADCAST_COUNT) / (duration / 1000) }, errors: broadcastsFailed > 0 ? [`${broadcastsFailed} broadcasts failed`] : [] }); console.log(`✓ Test duration: ${duration}ms`); console.log(`✓ Total messages: ${(NUM_CLIENTS * BROADCAST_COUNT).toLocaleString()}`); console.log(`✓ Broadcasts succeeded: ${broadcastsSucceeded}/${BROADCAST_COUNT}`); console.log(`✓ Messages/sec: ${((NUM_CLIENTS * BROADCAST_COUNT) / (duration / 1000)).toFixed(0)}`); } /** * Test 3: Sequential Animation Testing * Runs multiple Aurora timelines sequentially */ async testSequentialAnimations(): Promise<void> { console.log('\n🔄 Integration Test 3: Sequential Aurora Animation'); console.log('─'.repeat(60)); const NUM_TIMELINES = 100; const COMMANDS_PER_TIMELINE = 1000; const startTime = Date.now(); let successCount = 0; let failCount = 0; console.log(`Running ${NUM_TIMELINES} sequential animations...`); for (let timeline = 0; timeline < NUM_TIMELINES; timeline++) { try { // Generate timeline const commands = []; for (let i = 0; i < COMMANDS_PER_TIMELINE; i++) { commands.push({ timestamp: (i / COMMANDS_PER_TIMELINE) * 10, type: 'set_brightness', value: Math.floor(Math.random() * 255) }); } // Simulate execution with sliding window let queuedCount = 0; for (let i = 0; i < commands.length; i++) { const currentTime = (i / commands.length) * 10; const lookaheadTime = currentTime + 2.0; const queued = commands.filter( cmd => cmd.timestamp >= currentTime && cmd.timestamp <= lookaheadTime ); queuedCount = Math.max(queuedCount, queued.length); } if (queuedCount <= 5000) { // Queue stayed bounded successCount++; } else { failCount++; } if ((timeline + 1) % 20 === 0) { console.log(` Completed: ${timeline + 1}/${NUM_TIMELINES}`); } } catch (error) { failCount++; console.error(` Error in timeline ${timeline}:`, error); } } const duration = Date.now() - startTime; this.results.push({ name: 'Sequential Animations', passed: failCount === 0, duration, metrics: { totalTimelines: NUM_TIMELINES, successCount, failCount, commandsProcessed: NUM_TIMELINES * COMMANDS_PER_TIMELINE, timePerTimeline: duration / NUM_TIMELINES }, errors: failCount > 0 ? [`${failCount}/${NUM_TIMELINES} timelines failed`] : [] }); console.log(`✓ Test duration: ${duration}ms`); console.log(`✓ Animations succeeded: ${successCount}/${NUM_TIMELINES}`); console.log(`✓ Time per animation: ${(duration / NUM_TIMELINES).toFixed(1)}ms`); } /** * Test 4: Home Assistant API Resilience * Simulates API failures and recovery */ async testHAAPIResilience(): Promise<void> { console.log('\n🔄 Integration Test 4: Home Assistant API Resilience'); console.log('─'.repeat(60)); const TOTAL_CALLS = 1000; const FAILURE_RATE = 0.05; // 5% failure rate const startTime = Date.now(); let successCount = 0; let failureCount = 0; let recoveryCount = 0; let consecutiveFailures = 0; console.log(`Simulating ${TOTAL_CALLS} API calls with ${(FAILURE_RATE * 100).toFixed(1)}% failure rate...`); for (let i = 0; i < TOTAL_CALLS; i++) { const shouldFail = Math.random() < FAILURE_RATE; if (shouldFail) { failureCount++; consecutiveFailures++; // Try to recover if (consecutiveFailures < 5) { // Recovery attempt within threshold recoveryCount++; consecutiveFailures = 0; } } else { successCount++; consecutiveFailures = 0; } if ((i + 1) % 200 === 0) { console.log( ` Progress: ${i + 1}/${TOTAL_CALLS} ` + `(Success: ${successCount}, Failed: ${failureCount}, Recovered: ${recoveryCount})` ); } } const duration = Date.now() - startTime; const successRate = (successCount / TOTAL_CALLS) * 100; this.results.push({ name: 'HA API Resilience', passed: successRate >= 95, // Expect 95% success with failures duration, metrics: { totalCalls: TOTAL_CALLS, successCount, failureCount, recoveryCount, successRate, callsPerSecond: TOTAL_CALLS / (duration / 1000) }, errors: successRate < 95 ? [`Success rate ${successRate.toFixed(1)}% below 95% threshold`] : [] }); console.log(`✓ Test duration: ${duration}ms`); console.log(`✓ Success rate: ${successRate.toFixed(1)}%`); console.log(`✓ Recoveries: ${recoveryCount}`); console.log(`✓ Calls/sec: ${(TOTAL_CALLS / (duration / 1000)).toFixed(0)}`); } /** * Run all integration tests */ async runAll(): Promise<void> { console.log('\n🚀 Integration Test Suite'); console.log('═'.repeat(60)); console.log(`Started: ${new Date().toISOString()}`); try { await this.testStability24Hour(); await this.testLoadWith1000Clients(); await this.testSequentialAnimations(); await this.testHAAPIResilience(); this.printSummary(); } catch (error) { console.error('❌ Integration test failed:', error); process.exit(1); } } /** * Print summary report */ private printSummary(): void { console.log('\n📋 Integration Test Summary'); console.log('═'.repeat(60)); const passed = this.results.filter(r => r.passed).length; const total = this.results.length; console.log(`\nTests passed: ${passed}/${total}\n`); this.results.forEach(result => { const status = result.passed ? '✅' : '❌'; console.log(`${status} ${result.name}`); console.log(` Duration: ${result.duration}ms`); Object.entries(result.metrics).forEach(([key, value]) => { let formattedValue = value; if (typeof value === 'number') { formattedValue = value.toLocaleString(); } console.log(` ${key}: ${formattedValue}`); }); if (result.errors.length > 0) { console.log(` Errors: ${result.errors.join(', ')}`); } console.log(); }); console.log('═'.repeat(60)); if (passed === total) { console.log('✅ All integration tests passed!'); } else { console.log(`⚠️ ${total - passed} test(s) failed`); } console.log(`Completed: ${new Date().toISOString()}`); } } // Run tests const suite = new IntegrationTestSuite(); suite.runAll().catch(console.error);

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/jango-blockchained/advanced-homeassistant-mcp'

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