Skip to main content
Glama

In Memoria

test-server-lifecycle.js9.02 kB
#!/usr/bin/env node import { spawn } from 'child_process'; import { existsSync } from 'fs'; // Test server startup, shutdown, and cleanup behavior async function testServerLifecycle() { console.log('🔄 Testing MCP server lifecycle and cleanup...\n'); const tests = [ { name: 'Clean Startup', test: testCleanStartup }, { name: 'Graceful SIGTERM Shutdown', test: testGracefulShutdown }, { name: 'Force Kill Recovery', test: testForceKillRecovery }, { name: 'Resource Cleanup', test: testResourceCleanup }, { name: 'Database Lock Cleanup', test: testDatabaseLockCleanup } ]; const results = []; for (const test of tests) { console.log(`🧪 Testing: ${test.name}`); try { const result = await test.test(); results.push({ name: test.name, success: result.success, message: result.message }); console.log(`${result.success ? '✅' : '❌'} ${test.name}: ${result.message}\n`); } catch (error) { results.push({ name: test.name, success: false, message: `Test error: ${error.message}` }); console.log(`❌ ${test.name}: Test error: ${error.message}\n`); } // Wait between tests await new Promise(resolve => setTimeout(resolve, 2000)); } // Summary console.log('📊 Server Lifecycle Test Results:'); results.forEach(result => { const status = result.success ? '✅' : '❌'; console.log(`${status} ${result.name}: ${result.message}`); }); const successCount = results.filter(r => r.success).length; console.log(`\n🎯 Server Lifecycle: ${successCount}/${results.length} tests passed`); } async function testCleanStartup() { return new Promise((resolve) => { const server = spawn('node', ['dist/index.js', 'server'], { stdio: ['pipe', 'pipe', 'pipe'], cwd: process.cwd() }); let startupComplete = false; let timeout; server.stderr.on('data', (data) => { const output = data.toString(); if (output.includes('In Memoria MCP Server started')) { startupComplete = true; clearTimeout(timeout); server.kill('SIGTERM'); resolve({ success: true, message: 'Server started successfully' }); } }); server.on('error', (error) => { clearTimeout(timeout); resolve({ success: false, message: `Startup failed: ${error.message}` }); }); timeout = setTimeout(() => { if (!startupComplete) { server.kill('SIGKILL'); resolve({ success: false, message: 'Server startup timeout' }); } }, 10000); }); } async function testGracefulShutdown() { return new Promise((resolve) => { const server = spawn('node', ['dist/index.js', 'server'], { stdio: ['pipe', 'pipe', 'pipe'], cwd: process.cwd() }); let serverReady = false; let shutdownStartTime; server.stderr.on('data', (data) => { const output = data.toString(); if (output.includes('In Memoria MCP Server started') && !serverReady) { serverReady = true; // Give server a moment to fully initialize setTimeout(() => { shutdownStartTime = Date.now(); server.kill('SIGTERM'); // Graceful shutdown }, 1000); } }); server.on('exit', (code, signal) => { const shutdownTime = Date.now() - shutdownStartTime; // Graceful shutdown can result in code 0 with null signal when process.exit(0) is called if (code === 0 && (signal === 'SIGTERM' || signal === null)) { resolve({ success: true, message: `Graceful shutdown completed in ${shutdownTime}ms` }); } else { resolve({ success: false, message: `Unexpected shutdown: code=${code}, signal=${signal}` }); } }); setTimeout(() => { server.kill('SIGKILL'); resolve({ success: false, message: 'Graceful shutdown timeout' }); }, 15000); }); } async function testForceKillRecovery() { return new Promise((resolve) => { // First, force kill a server const server1 = spawn('node', ['dist/index.js', 'server'], { stdio: ['pipe', 'pipe', 'pipe'], cwd: process.cwd() }); server1.stderr.on('data', (data) => { if (data.toString().includes('In Memoria MCP Server started')) { // Force kill the server server1.kill('SIGKILL'); // Wait a moment, then try to start another server setTimeout(() => { const server2 = spawn('node', ['dist/index.js', 'server'], { stdio: ['pipe', 'pipe', 'pipe'], cwd: process.cwd() }); server2.stderr.on('data', (data) => { if (data.toString().includes('In Memoria MCP Server started')) { server2.kill('SIGTERM'); resolve({ success: true, message: 'Successfully recovered from force kill' }); } }); server2.on('error', (error) => { resolve({ success: false, message: `Recovery failed: ${error.message}` }); }); setTimeout(() => { server2.kill('SIGKILL'); resolve({ success: false, message: 'Recovery startup timeout' }); }, 10000); }, 2000); } }); server1.on('error', (error) => { resolve({ success: false, message: `Initial server error: ${error.message}` }); }); }); } async function testResourceCleanup() { return new Promise((resolve) => { const server = spawn('node', ['dist/index.js', 'server'], { stdio: ['pipe', 'pipe', 'pipe'], cwd: process.cwd() }); let initialMemory; server.stderr.on('data', (data) => { if (data.toString().includes('In Memoria MCP Server started')) { // Get initial memory usage initialMemory = process.memoryUsage(); // Shutdown after a moment setTimeout(() => { server.kill('SIGTERM'); }, 2000); } }); server.on('exit', () => { // Check memory after shutdown setTimeout(() => { const finalMemory = process.memoryUsage(); const memoryIncrease = finalMemory.heapUsed - initialMemory.heapUsed; // Allow some memory increase but flag if excessive if (memoryIncrease < 50 * 1024 * 1024) { // Less than 50MB increase resolve({ success: true, message: `Memory cleanup OK (${Math.round(memoryIncrease/1024/1024)}MB increase)` }); } else { resolve({ success: false, message: `Possible memory leak (${Math.round(memoryIncrease/1024/1024)}MB increase)` }); } }, 1000); }); setTimeout(() => { server.kill('SIGKILL'); resolve({ success: false, message: 'Resource cleanup test timeout' }); }, 15000); }); } async function testDatabaseLockCleanup() { return new Promise((resolve) => { const server1 = spawn('node', ['dist/index.js', 'server'], { stdio: ['pipe', 'pipe', 'pipe'], cwd: process.cwd() }); server1.stderr.on('data', (data) => { if (data.toString().includes('In Memoria MCP Server started')) { // Terminate the first server server1.kill('SIGTERM'); } }); server1.on('exit', () => { // Immediately try to start another server to test database lock cleanup setTimeout(() => { const server2 = spawn('node', ['dist/index.js', 'server'], { stdio: ['pipe', 'pipe', 'pipe'], cwd: process.cwd() }); server2.stderr.on('data', (data) => { if (data.toString().includes('In Memoria MCP Server started')) { server2.kill('SIGTERM'); resolve({ success: true, message: 'Database locks properly cleaned up' }); } if (data.toString().includes('database is locked')) { server2.kill('SIGKILL'); resolve({ success: false, message: 'Database lock not cleaned up properly' }); } }); server2.on('error', (error) => { resolve({ success: false, message: `Database lock test failed: ${error.message}` }); }); setTimeout(() => { server2.kill('SIGKILL'); resolve({ success: false, message: 'Database lock test timeout' }); }, 10000); }, 1000); }); setTimeout(() => { server1.kill('SIGKILL'); resolve({ success: false, message: 'Database lock cleanup test timeout' }); }, 20000); }); } // Run the lifecycle tests testServerLifecycle().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/pi22by7/In-Memoria'

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