/**
* Startup Performance Tests
*
* Validates that the MCP server meets startup time requirements
* as specified in the Design Doc (≤3 seconds).
*/
import { ChildProcess, spawn } from 'node:child_process'
import fs from 'node:fs/promises'
import { tmpdir } from 'node:os'
import path from 'node:path'
import { ServerConfig } from 'src/config/ServerConfig'
import { McpServer } from 'src/server/McpServer'
import { afterAll, beforeAll, describe, expect, test } from 'vitest'
describe('Startup Performance Tests', () => {
let testAgentsDir: string
beforeAll(async () => {
// Setup test environment
testAgentsDir = path.join(tmpdir(), 'mcp-startup-test-agents')
await fs.mkdir(testAgentsDir, { recursive: true })
// Create several test agent files to simulate realistic load
for (let i = 1; i <= 10; i++) {
await fs.writeFile(
path.join(testAgentsDir, `test-agent-${i}.md`),
`# Test Agent ${i}\n\nAgent ${i} for startup performance testing.\n\nUsage: echo "Agent ${i} ready"`
)
}
})
afterAll(async () => {
// Cleanup test agents directory
await fs.rm(testAgentsDir, { recursive: true, force: true })
})
test('server startup time meets 3-second requirement', async () => {
const startTime = Date.now()
// Set test environment variables
const testEnv = {
...process.env,
SERVER_NAME: 'startup-performance-test',
AGENTS_DIR: testAgentsDir,
}
// Start server process
const serverPath = path.join(__dirname, '../../../dist/index.js')
const serverProcess = spawn('node', [serverPath], {
stdio: ['pipe', 'pipe', 'pipe'],
env: testEnv,
})
try {
// Wait for server to be ready
const startupComplete = await new Promise<number>((resolve, reject) => {
const timeout = setTimeout(() => {
reject(new Error('Startup timeout exceeded 5 seconds'))
}, 5000)
// MCP server logs to stderr (MCP protocol compliance)
serverProcess.stderr?.on('data', (data) => {
const output = data.toString()
if (
output.includes('MCP server started') ||
output.includes('listening') ||
output.includes('ready')
) {
const endTime = Date.now()
clearTimeout(timeout)
resolve(endTime)
}
})
serverProcess.stdout?.on('data', (data) => {
// stdout should only contain JSON-RPC messages in MCP protocol
// Log for debugging if anything unexpected appears
const stdoutData = data.toString()
if (stdoutData.trim()) {
console.warn('Unexpected stdout output:', stdoutData)
}
})
serverProcess.on('error', (error) => {
clearTimeout(timeout)
reject(error)
})
serverProcess.on('exit', (code) => {
clearTimeout(timeout)
if (code !== 0) {
reject(new Error(`Server exited with code ${code}`))
}
})
})
const startupTime = startupComplete - startTime
// Verify startup time requirement
expect(startupTime).toBeLessThan(3000) // 3 seconds max
// Performance metric captured in test assertion above
// expect(startupTime).toBeLessThan(3000)
} finally {
// Clean up server process
serverProcess.kill('SIGTERM')
await new Promise((resolve) => {
serverProcess.on('exit', resolve)
setTimeout(() => {
serverProcess.kill('SIGKILL')
resolve(undefined)
}, 1000)
})
}
})
test('server startup time with large agent directory (stress test)', async () => {
// Create larger test directory for stress testing
const stressTestDir = path.join(tmpdir(), 'mcp-startup-stress-test')
await fs.mkdir(stressTestDir, { recursive: true })
try {
// Create 50 agent files to test scalability
for (let i = 1; i <= 50; i++) {
await fs.writeFile(
path.join(stressTestDir, `stress-agent-${i}.md`),
`# Stress Test Agent ${i}\n\nAgent ${i} for stress testing startup performance.\n\nUsage: echo "Stress agent ${i} ready"`
)
}
const startTime = Date.now()
const testEnv = {
...process.env,
SERVER_NAME: 'startup-stress-test',
AGENTS_DIR: stressTestDir,
}
const serverPath = path.join(__dirname, '../../../dist/index.js')
const serverProcess = spawn('node', [serverPath], {
stdio: ['pipe', 'pipe', 'pipe'],
env: testEnv,
})
try {
const startupComplete = await new Promise<number>((resolve, reject) => {
const timeout = setTimeout(() => {
reject(new Error('Stress test startup timeout exceeded 5 seconds'))
}, 5000)
// MCP server logs to stderr (MCP protocol compliance)
serverProcess.stderr?.on('data', (data) => {
const output = data.toString()
if (
output.includes('MCP server started') ||
output.includes('listening') ||
output.includes('ready')
) {
const endTime = Date.now()
clearTimeout(timeout)
resolve(endTime)
}
})
serverProcess.stdout?.on('data', (data) => {
// stdout should only contain JSON-RPC messages in MCP protocol
// Log for debugging if anything unexpected appears
const stdoutData = data.toString()
if (stdoutData.trim()) {
console.warn('Unexpected stdout output:', stdoutData)
}
})
serverProcess.on('error', reject)
})
const stressStartupTime = startupComplete - startTime
// Even with 50 agents, should still meet startup requirement
expect(stressStartupTime).toBeLessThan(3000)
// Performance metric already verified in assertion above
// expect(stressStartupTime).toBeLessThan(3000)
} finally {
serverProcess.kill('SIGTERM')
await new Promise((resolve) => {
serverProcess.on('exit', resolve)
setTimeout(() => {
serverProcess.kill('SIGKILL')
resolve(undefined)
}, 1000)
})
}
} finally {
// Cleanup stress test directory
await fs.rm(stressTestDir, { recursive: true, force: true })
}
})
test('server startup performance with minimal configuration', async () => {
// Test minimal configuration startup performance
const startTime = Date.now()
const minimalEnv = {
...process.env,
SERVER_NAME: 'minimal-startup-test',
AGENTS_DIR: testAgentsDir, // Use basic test directory
}
// Set environment variables
process.env.SERVER_NAME = minimalEnv.SERVER_NAME
process.env.AGENTS_DIR = minimalEnv.AGENTS_DIR
const config = new ServerConfig()
const server = new McpServer(config)
try {
await server.start()
const startupTime = Date.now() - startTime
// Should start very quickly with minimal config
expect(startupTime).toBeLessThan(1000) // 1 second for minimal setup
// Performance metric already verified in assertion above
// expect(startupTime).toBeLessThan(1000)
} finally {
await server.close()
}
})
test('concurrent startup requests handling', async () => {
// Test that server can handle multiple simultaneous startup requests
const startTime = Date.now()
const testEnv = {
...process.env,
SERVER_NAME: 'concurrent-startup-test',
AGENTS_DIR: testAgentsDir,
}
// Start multiple server configurations simultaneously
const originalEnv = { ...process.env }
Object.assign(process.env, testEnv)
const config1 = new ServerConfig()
process.env.SERVER_NAME = 'concurrent-test-2'
const config2 = new ServerConfig()
process.env.SERVER_NAME = 'concurrent-test-3'
const config3 = new ServerConfig()
// Restore original environment
Object.assign(process.env, originalEnv)
const configs = [config1, config2, config3]
const servers = configs.map((config) => new McpServer(config))
try {
// Start all servers concurrently
await Promise.all(servers.map((server) => server.start()))
const concurrentStartupTime = Date.now() - startTime
// Even with concurrent initialization, should meet startup requirements
expect(concurrentStartupTime).toBeLessThan(3000)
// Performance metric already verified in assertion above
// expect(concurrentStartupTime).toBeLessThan(3000)
} finally {
// Clean up all servers
await Promise.all(servers.map((server) => server.close()))
}
})
test('startup performance with environment variable loading', async () => {
// Test that environment variable processing doesn't significantly impact startup
const startTime = Date.now()
// Set many environment variables to test processing overhead
const heavyEnv = {
...process.env,
SERVER_NAME: 'env-heavy-test',
AGENTS_DIR: testAgentsDir,
// Add extra variables to test processing
TEST_VAR_1: 'value1',
TEST_VAR_2: 'value2',
TEST_VAR_3: 'value3',
MAX_CONCURRENT_EXECUTIONS: '5',
MAX_OUTPUT_SIZE: '1048576',
PROMPT_ENHANCEMENT_ENABLED: 'true',
}
// Set heavy environment variables
Object.assign(process.env, heavyEnv)
const config = new ServerConfig()
const configLoadTime = Date.now() - startTime
// Configuration loading should be very fast
expect(configLoadTime).toBeLessThan(100) // 100ms max for config loading
const server = new McpServer(config)
await server.start()
const totalStartupTime = Date.now() - startTime
expect(totalStartupTime).toBeLessThan(3000)
// Performance metric already verified in assertion above
// expect(totalStartupTime).toBeLessThan(3000)
await server.close()
})
})