Skip to main content
Glama

Magic-API MCP Server

by Dwsy
test-runner.js23.8 kB
#!/usr/bin/env node /** * Magic-API MCP 工具链自动化测试执行器 * * 功能特性: * - 分阶段执行测试 * - 自动验证结果 * - 性能指标收集 * - 详细日志记录 * - 错误处理和恢复 */ const fs = require('fs'); const path = require('path'); // 测试配置 const TEST_CONFIG = { baseUrl: 'http://127.0.0.1:10712', timeout: 10000, retries: 3, logLevel: 'info' }; // 测试结果存储 let testResults = { startTime: new Date().toISOString(), phases: {}, summary: { totalTests: 0, passed: 0, failed: 0, skipped: 0 } }; /** * 日志记录函数 */ function log(level, message, data = null) { const timestamp = new Date().toISOString(); const logEntry = { timestamp, level, message, ...(data && { data }) }; console.log(`[${timestamp}] [${level.toUpperCase()}] ${message}`); if (data) { console.log(JSON.stringify(data, null, 2)); } } /** * 模拟 MCP 工具调用 * 注意:这里是模拟实现,实际需要集成真实的 MCP 客户端 */ async function mockMcpCall(toolName, params = {}) { log('debug', `执行 MCP 调用: ${toolName}`, params); // 模拟响应延迟 await new Promise(resolve => setTimeout(resolve, Math.random() * 500 + 100)); // 模拟响应 const mockResponses = { 'get_assistant_metadata': { system_prompt: 'Mock system prompt', version: '2.2.0', features: ['syntax', 'examples', 'docs'], environment: { base_url: TEST_CONFIG.baseUrl, auth_enabled: false } }, 'list_available_modules': { all_modules: ['db', 'http', 'response', 'request', 'log', 'env', 'magic'], auto_import_modules: ['db', 'log'], module_count: 7 }, 'get_resource_tree': { kind: 'all', count: 0, nodes: [], filters_applied: {} }, 'list_backups': { total_backups: 100, filtered_backups: 0, returned_backups: 0, limit: 10, filters_applied: {}, backups: [] }, 'get_full_magic_script_syntax': { language: 'magic-script', version: 'latest', sections: { keywords: { title: '关键字', items: ['var', 'if', 'for'] }, operators: { title: '运算符', math: ['+', '-', '*'] } } }, 'get_development_workflow': { task: 'api_script_development', steps: ['资源定位', '语法对齐', '脚本准备', '功能验证'], core_workflow_overview: { summary: '核心工作流描述' } }, 'search_api_endpoints': [], 'search_api_scripts': [], 'search_todo_comments': [], 'search_knowledge': [], 'get_debug_status': { status: 'disconnected', active_sessions: 0 }, 'get_websocket_status': { connected: false, url: 'ws://127.0.0.1:10712/magic/web/console' }, 'get_resource_statistics': { total_apis: 0, total_groups: 0, total_functions: 0 }, 'list_resource_groups': [], 'export_resource_tree': { format: 'json', data: {} }, 'get_api_details_by_path': { id: null, path: '/test/api', method: null }, 'get_backup_history': { backup_id: 'test-backup-123', history: [] }, 'get_backup_content': { backup_id: 'test-backup-123', timestamp: Date.now(), content: {} }, 'get_best_practices': { performance: {}, security: {}, debugging: {} }, 'get_common_pitfalls': { common_issues: [], best_practices: [] }, 'get_knowledge_overview': { total_categories: 5, total_entries: 100, version: '2.2.0' } }; return mockResponses[toolName] || { error: { code: 'not_implemented', message: `工具 ${toolName} 未实现` } }; } /** * 执行单个测试用例 */ async function executeTest(testName, testFunction) { log('info', `开始执行测试: ${testName}`); const startTime = Date.now(); let attempts = 0; let lastError = null; while (attempts < TEST_CONFIG.retries) { attempts++; try { const result = await testFunction(); const duration = Date.now() - startTime; log('info', `测试 ${testName} 成功完成`, { duration, attempts }); return { success: true, result, duration, attempts }; } catch (error) { lastError = error; log('warn', `测试 ${testName} 第 ${attempts} 次尝试失败: ${error.message}`); if (attempts < TEST_CONFIG.retries) { await new Promise(resolve => setTimeout(resolve, 1000 * attempts)); } } } log('error', `测试 ${testName} 最终失败`, { lastError: lastError.message }); return { success: false, error: lastError, duration: Date.now() - startTime, attempts }; } /** * 阶段1:基础信息收集与验证 */ async function phase1_BasicInfoTests() { log('info', '=== 开始阶段1:基础信息收集与验证 ==='); const phase = { name: '基础信息收集与验证', tests: {}, summary: { passed: 0, failed: 0, total: 0 } }; // 测试1.1: 系统元信息验证 phase.tests.get_assistant_metadata = await executeTest('get_assistant_metadata', async () => { const result = await mockMcpCall('get_assistant_metadata'); if (!result.version || !result.features) { throw new Error('响应格式不正确'); } return result; }); // 测试1.2: 可用模块列表验证 phase.tests.list_available_modules = await executeTest('list_available_modules', async () => { const result = await mockMcpCall('list_available_modules'); if (!result.all_modules || !Array.isArray(result.all_modules)) { throw new Error('模块列表格式不正确'); } return result; }); // 测试1.3: 资源树结构检查 phase.tests.get_resource_tree = await executeTest('get_resource_tree', async () => { const result = await mockMcpCall('get_resource_tree'); if (result.count !== 0) { log('warn', '资源树不为空,可能存在测试数据'); } return result; }); // 测试1.4: 备份功能可用性验证 phase.tests.list_backups = await executeTest('list_backups', async () => { const result = await mockMcpCall('list_backups'); if (typeof result.total_backups !== 'number') { throw new Error('备份统计数据格式不正确'); } return result; }); // 统计结果 phase.summary.total = Object.keys(phase.tests).length; phase.summary.passed = Object.values(phase.tests).filter(t => t.success).length; phase.summary.failed = phase.summary.total - phase.summary.passed; testResults.phases.phase1 = phase; log('info', `阶段1完成: ${phase.summary.passed}/${phase.summary.total} 通过`); } /** * 阶段2:文档查询功能测试 */ async function phase2_DocumentationTests() { log('info', '=== 开始阶段2:文档查询功能测试 ==='); const phase = { name: '文档查询功能测试', tests: {}, summary: { passed: 0, failed: 0, total: 0 } }; // 测试2.1: 完整语法规则获取 phase.tests.get_full_magic_script_syntax = await executeTest('get_full_magic_script_syntax', async () => { const result = await mockMcpCall('get_full_magic_script_syntax'); if (!result.language || !result.sections) { throw new Error('语法规则响应格式不正确'); } return result; }); // 测试2.2: 开发工作流指南 phase.tests.get_development_workflow = await executeTest('get_development_workflow', async () => { const result = await mockMcpCall('get_development_workflow'); if (!result.task || !result.steps) { throw new Error('开发工作流响应格式不正确'); } return result; }); // 统计结果 phase.summary.total = Object.keys(phase.tests).length; phase.summary.passed = Object.values(phase.tests).filter(t => t.success).length; phase.summary.failed = phase.summary.total - phase.summary.passed; testResults.phases.phase2 = phase; log('info', `阶段2完成: ${phase.summary.passed}/${phase.summary.total} 通过`); } /** * 阶段3:搜索与查询功能测试 */ async function phase3_SearchAndQueryTests() { log('info', '=== 开始阶段3:搜索与查询功能测试 ==='); const phase = { name: '搜索与查询功能测试', tests: {}, summary: { passed: 0, failed: 0, total: 0 } }; // 测试3.1: 资源搜索功能 phase.tests.search_api_endpoints = await executeTest('search_api_endpoints', async () => { const result = await mockMcpCall('search_api_endpoints', { method_filter: null, path_filter: null }); if (!Array.isArray(result)) { throw new Error('搜索结果应为数组格式'); } return result; }); // 测试3.2: 资源树深度搜索 phase.tests.get_resource_tree_with_depth = await executeTest('get_resource_tree_with_depth', async () => { const result = await mockMcpCall('get_resource_tree', { depth: 2 }); if (typeof result.count !== 'number') { throw new Error('资源树统计数据格式不正确'); } return result; }); // 测试3.3: 脚本内容搜索 phase.tests.search_api_scripts = await executeTest('search_api_scripts', async () => { const result = await mockMcpCall('search_api_scripts', { keyword: 'test', limit: 5 }); if (!Array.isArray(result)) { throw new Error('脚本搜索结果应为数组格式'); } return result; }); // 测试3.4: TODO 注释搜索 phase.tests.search_todo_comments = await executeTest('search_todo_comments', async () => { const result = await mockMcpCall('search_todo_comments', { keyword: 'TODO', limit: 10 }); if (!Array.isArray(result)) { throw new Error('TODO 搜索结果应为数组格式'); } return result; }); // 测试3.5: 知识库搜索功能 phase.tests.search_knowledge_basic = await executeTest('search_knowledge_basic', async () => { const result = await mockMcpCall('search_knowledge', { keyword: '数据库' }); if (!Array.isArray(result)) { throw new Error('知识库搜索结果应为数组格式'); } return result; }); // 测试3.6: 知识库分类搜索 phase.tests.search_knowledge_category = await executeTest('search_knowledge_category', async () => { const result = await mockMcpCall('search_knowledge', { keyword: '语法', category: 'syntax' }); if (!Array.isArray(result)) { throw new Error('分类搜索结果应为数组格式'); } return result; }); // 统计结果 phase.summary.total = Object.keys(phase.tests).length; phase.summary.passed = Object.values(phase.tests).filter(t => t.success).length; phase.summary.failed = phase.summary.total - phase.summary.passed; testResults.phases.phase3 = phase; log('info', `阶段3完成: ${phase.summary.passed}/${phase.summary.total} 通过`); } /** * 阶段4:API 调用与调试功能测试 */ async function phase4_ApiAndDebugTests() { log('info', '=== 开始阶段4:API 调用与调试功能测试 ==='); const phase = { name: 'API 调用与调试功能测试', tests: {}, summary: { passed: 0, failed: 0, total: 0 } }; // 测试4.1: 调试状态检查 phase.tests.get_debug_status = await executeTest('get_debug_status', async () => { const result = await mockMcpCall('get_debug_status'); if (!result || typeof result !== 'object') { throw new Error('调试状态响应格式不正确'); } return result; }); // 测试4.2: WebSocket 连接状态 phase.tests.get_websocket_status = await executeTest('get_websocket_status', async () => { const result = await mockMcpCall('get_websocket_status'); if (!result || typeof result !== 'object') { throw new Error('WebSocket 状态响应格式不正确'); } return result; }); // 测试4.3: 资源统计功能 phase.tests.get_resource_statistics = await executeTest('get_resource_statistics', async () => { const result = await mockMcpCall('get_resource_statistics'); if (!result || typeof result !== 'object') { throw new Error('资源统计响应格式不正确'); } return result; }); // 测试4.4: 资源分组列表 phase.tests.list_resource_groups = await executeTest('list_resource_groups', async () => { const result = await mockMcpCall('list_resource_groups', { limit: 10 }); if (!Array.isArray(result)) { throw new Error('资源分组列表应为数组格式'); } return result; }); // 测试4.5: 资源树导出功能 phase.tests.export_resource_tree = await executeTest('export_resource_tree', async () => { const result = await mockMcpCall('export_resource_tree', { format: 'json' }); if (!result || typeof result !== 'object') { throw new Error('资源树导出响应格式不正确'); } return result; }); // 测试4.6: 路径查询测试 phase.tests.get_api_details_by_path = await executeTest('get_api_details_by_path', async () => { const result = await mockMcpCall('get_api_details_by_path', { path: '/test/api' }); if (!result || typeof result !== 'object') { throw new Error('API 详情响应格式不正确'); } return result; }); // 统计结果 phase.summary.total = Object.keys(phase.tests).length; phase.summary.passed = Object.values(phase.tests).filter(t => t.success).length; phase.summary.failed = phase.summary.total - phase.summary.passed; testResults.phases.phase4 = phase; log('info', `阶段4完成: ${phase.summary.passed}/${phase.summary.total} 通过`); } /** * 阶段5:备份与恢复功能测试 */ async function phase5_BackupAndRestoreTests() { log('info', '=== 开始阶段5:备份与恢复功能测试 ==='); const phase = { name: '备份与恢复功能测试', tests: {}, summary: { passed: 0, failed: 0, total: 0 } }; // 测试5.1: 获取备份历史记录 phase.tests.get_backup_history = await executeTest('get_backup_history', async () => { const result = await mockMcpCall('get_backup_history', { backup_id: 'test-backup-123' }); if (!result || typeof result !== 'object') { throw new Error('备份历史响应格式不正确'); } return result; }); // 测试5.2: 获取备份内容 phase.tests.get_backup_content = await executeTest('get_backup_content', async () => { const result = await mockMcpCall('get_backup_content', { backup_id: 'test-backup-123', timestamp: Date.now() }); if (!result || typeof result !== 'object') { throw new Error('备份内容响应格式不正确'); } return result; }); // 测试5.3: 高级备份功能测试 phase.tests.list_backups_advanced = await executeTest('list_backups_advanced', async () => { const result = await mockMcpCall('list_backups', { timestamp: Date.now() - 86400000, // 24小时前 filter_text: 'test', limit: 5 }); if (!result || typeof result !== 'object') { throw new Error('高级备份查询响应格式不正确'); } return result; }); // 测试5.4: 最佳实践指南 phase.tests.get_best_practices = await executeTest('get_best_practices', async () => { const result = await mockMcpCall('get_best_practices'); if (!result || typeof result !== 'object') { throw new Error('最佳实践响应格式不正确'); } return result; }); // 测试5.5: 常见陷阱信息 phase.tests.get_common_pitfalls = await executeTest('get_common_pitfalls', async () => { const result = await mockMcpCall('get_common_pitfalls'); if (!result || typeof result !== 'object') { throw new Error('常见陷阱响应格式不正确'); } return result; }); // 测试5.6: 知识库概览 phase.tests.get_knowledge_overview = await executeTest('get_knowledge_overview', async () => { const result = await mockMcpCall('get_knowledge_overview'); if (!result || typeof result !== 'object') { throw new Error('知识库概览响应格式不正确'); } return result; }); // 统计结果 phase.summary.total = Object.keys(phase.tests).length; phase.summary.passed = Object.values(phase.tests).filter(t => t.success).length; phase.summary.failed = phase.summary.total - phase.summary.passed; testResults.phases.phase5 = phase; log('info', `阶段5完成: ${phase.summary.passed}/${phase.summary.total} 通过`); } /** * 阶段6:高级功能与边界测试 */ async function phase6_AdvancedAndBoundaryTests() { log('info', '=== 开始阶段6:高级功能与边界测试 ==='); const phase = { name: '高级功能与边界测试', tests: {}, summary: { passed: 0, failed: 0, total: 0 } }; // 测试6.1: 无效参数处理 phase.tests.invalid_params_handling = await executeTest('invalid_params_handling', async () => { const result = await mockMcpCall('search_knowledge', { keyword: '' }); if (!result || typeof result !== 'object') { throw new Error('无效参数处理响应格式不正确'); } return result; }); // 测试6.2: 空结果集处理 phase.tests.empty_results_handling = await executeTest('empty_results_handling', async () => { const result = await mockMcpCall('search_api_scripts', { keyword: 'nonexistent-keyword-12345' }); if (!Array.isArray(result)) { throw new Error('空结果集处理响应格式不正确'); } return result; }); // 测试6.3: 大量数据查询(模拟) phase.tests.large_data_query = await executeTest('large_data_query', async () => { const result = await mockMcpCall('get_full_magic_script_syntax'); if (!result.sections || typeof result.sections !== 'object') { throw new Error('大量数据查询响应格式不正确'); } return result; }); // 测试6.4: 并发调用测试(模拟) phase.tests.concurrent_calls = await executeTest('concurrent_calls', async () => { const promises = Array(3).fill().map(() => mockMcpCall('get_assistant_metadata')); const results = await Promise.all(promises); if (!Array.isArray(results) || results.length !== 3) { throw new Error('并发调用测试失败'); } return { concurrent_results: results.length }; }); // 测试6.5: 错误响应处理 phase.tests.error_response_handling = await executeTest('error_response_handling', async () => { const result = await mockMcpCall('nonexistent_tool'); if (!result.error || !result.error.code) { throw new Error('错误响应处理测试失败'); } return result; }); // 测试6.6: 性能基准测试(响应时间) phase.tests.performance_benchmark = await executeTest('performance_benchmark', async () => { const startTime = Date.now(); await mockMcpCall('get_assistant_metadata'); const duration = Date.now() - startTime; if (duration > 2000) { log('warn', `性能基准测试警告: 响应时间过长 (${duration}ms)`); } return { response_time: duration }; }); // 统计结果 phase.summary.total = Object.keys(phase.tests).length; phase.summary.passed = Object.values(phase.tests).filter(t => t.success).length; phase.summary.failed = phase.summary.total - phase.summary.passed; testResults.phases.phase6 = phase; log('info', `阶段6完成: ${phase.summary.passed}/${phase.summary.total} 通过`); } /** * 生成测试报告 */ function generateReport() { const reportPath = path.join(__dirname, 'test-report.json'); const summaryPath = path.join(__dirname, 'test-summary.md'); // 计算总体统计 let totalTests = 0; let totalPassed = 0; let totalFailed = 0; Object.values(testResults.phases).forEach(phase => { totalTests += phase.summary.total; totalPassed += phase.summary.passed; totalFailed += phase.summary.failed; }); testResults.summary = { totalTests, passed: totalPassed, failed: totalFailed, skipped: 0, successRate: totalTests > 0 ? (totalPassed / totalTests * 100).toFixed(2) + '%' : '0%' }; testResults.endTime = new Date().toISOString(); // 保存详细报告 fs.writeFileSync(reportPath, JSON.stringify(testResults, null, 2)); log('info', `详细测试报告已保存到: ${reportPath}`); // 生成摘要报告 const summaryContent = `# Magic-API MCP 工具链测试报告 ## 📊 测试摘要 - **测试时间**: ${testResults.startTime} - ${testResults.endTime} - **总体通过率**: ${testResults.summary.successRate} - **测试用例**: ${totalPassed}/${totalTests} 通过 ## 📋 分阶段结果 ${Object.entries(testResults.phases).map(([phaseKey, phase]) => `### ${phase.name} - 通过: ${phase.summary.passed} - 失败: ${phase.summary.failed} - 总计: ${phase.summary.total} ` ).join('\n')} ## 🎯 测试结论 ${totalFailed === 0 ? '✅ 所有测试通过,MCP 工具链功能正常' : '⚠️ 部分测试失败,需要进一步检查'} --- *报告生成时间: ${new Date().toISOString()}* `; fs.writeFileSync(summaryPath, summaryContent); log('info', `测试摘要报告已保存到: ${summaryPath}`); } /** * 主测试执行函数 */ async function runAllTests() { try { log('info', '🚀 开始 Magic-API MCP 工具链全面测试'); // 执行各阶段测试 await phase1_BasicInfoTests(); await phase2_DocumentationTests(); await phase3_SearchAndQueryTests(); await phase4_ApiAndDebugTests(); await phase5_BackupAndRestoreTests(); await phase6_AdvancedAndBoundaryTests(); // 生成报告 generateReport(); const successRate = testResults.summary.successRate; log('info', `🎉 测试完成!总体通过率: ${successRate}`); // 根据结果给出建议 if (successRate === '100.00%') { log('info', '✅ 所有测试通过,MCP 工具链运行正常'); } else { log('warn', '⚠️ 部分测试失败,建议检查相关功能'); } } catch (error) { log('error', `测试执行过程中发生错误: ${error.message}`, error.stack); process.exit(1); } } // 导出测试函数 module.exports = { runAllTests, executeTest, mockMcpCall, log }; // 如果直接运行此脚本 if (require.main === module) { runAllTests(); }

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/Dwsy/magic-api-mcp-server'

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