test-server-tools-actual.js•13.4 kB
#!/usr/bin/env node
/**
* Actual Server Tools Test
* Tests the actual tools registered by the MCP server (based on server logs)
* This test reflects what the server ACTUALLY registers, not what we expect
*/
import { spawn } from 'child_process';
import chalk from 'chalk';
export class ActualServerToolsTest {
constructor(options = {}) {
this.verbose = options.verbose || false;
this.timeout = options.timeout || 15000;
this.results = {
passed: 0,
failed: 0,
tests: []
};
// ACTUAL tools registered by server (from logs analysis)
this.actualTools = [
// ===== DISCOVERY TOOLS (4) =====
'search-sap-services',
'discover-service-entities',
'get-entity-schema',
'execute-entity-operation',
// ===== AUTH & ROUTING (2) =====
'check-sap-authentication',
'sap-smart-query',
// ===== AI PHASE 2 (4) =====
'natural-query-builder',
'smart-data-analysis',
'query-performance-optimizer',
'business-process-insights',
// ===== REALTIME PHASE 3 (4) =====
'realtime-data-stream',
'kpi-dashboard-builder',
'predictive-analytics-engine',
'business-intelligence-insights'
];
this.serverLogMessages = [
'✅ Registered 4 hierarchical discovery tools successfully',
'✅ Registered Authentication Check Tool: check-sap-authentication',
'✅ Registered Smart Router Tool: sap-smart-query',
'✅ Registered 8 AI-Enhanced tools: 4 Phase 2 + 4 Phase 3 Real-time Analytics tools',
'🎯 TOTAL TOOLS REGISTERED: 12 (4 Discovery + 4 AI Phase 2 + 4 Real-time Phase 3)'
];
}
log(message, type = 'info') {
if (!this.verbose && type === 'debug') return;
const prefix = {
info: chalk.blue('ℹ'),
success: chalk.green('✅'),
error: chalk.red('❌'),
debug: chalk.gray('🔍'),
warning: chalk.yellow('⚠️')
}[type] || '';
console.log(`${prefix} ${message}`);
}
async runTest() {
this.log('🔍 Testing ACTUAL server tool registration (log-based analysis)', 'info');
this.log(`📊 Expected tools from server logs: ${this.actualTools.length}`, 'info');
try {
// Test 1: Server startup and log analysis
const startupResult = await this.testServerStartupLogs();
this.recordResult('Server Startup & Log Analysis', startupResult);
// Test 2: Tools list extraction from server
const toolsListResult = await this.testActualToolsList();
this.recordResult('Actual Tools List Extraction', toolsListResult);
return this.results;
} catch (error) {
this.log(`Test suite failed: ${error.message}`, 'error');
return this.results;
}
}
testServerStartupLogs() {
return new Promise((resolve) => {
this.log('🚀 Starting server and analyzing logs...', 'debug');
const child = spawn('node', ['dist/mcp-server.js'], {
stdio: ['pipe', 'pipe', 'pipe'],
env: { ...process.env, NODE_ENV: 'test' }
});
let stdout = '';
let stderr = '';
let foundLogMessages = [];
const timeout = setTimeout(() => {
child.kill();
const foundCount = foundLogMessages.length;
const expectedCount = this.serverLogMessages.length;
resolve({
success: foundCount >= 3, // At least 3 out of 5 key messages
foundLogMessages,
stdout: stdout.substring(0, 2000), // Truncate for readability
stderr: stderr.substring(0, 1000),
foundCount,
expectedCount,
analysis: this.analyzeServerLogs(stdout, stderr)
});
}, this.timeout);
child.stdout.on('data', (data) => {
stdout += data.toString();
});
child.stderr.on('data', (data) => {
stderr += data.toString();
});
child.on('close', () => {
clearTimeout(timeout);
});
// Check for key log messages
const checkLogs = () => {
this.serverLogMessages.forEach(message => {
if ((stdout + stderr).includes(message) && !foundLogMessages.includes(message)) {
foundLogMessages.push(message);
this.log(`Found: ${message}`, 'debug');
}
});
};
const logCheckInterval = setInterval(checkLogs, 500);
setTimeout(() => clearInterval(logCheckInterval), this.timeout);
});
}
testActualToolsList() {
return new Promise((resolve) => {
this.log('📋 Extracting actual tools list from server...', 'debug');
const child = spawn('node', ['dist/mcp-server.js'], {
stdio: ['pipe', 'pipe', 'pipe']
});
let output = '';
let actualToolsList = [];
const timeout = setTimeout(() => {
child.kill();
resolve({
success: actualToolsList.length > 0,
actualToolsList,
expectedCount: this.actualTools.length,
foundCount: actualToolsList.length,
missingTools: this.actualTools.filter(name => !actualToolsList.includes(name)),
extraTools: actualToolsList.filter(name => !this.actualTools.includes(name))
});
}, this.timeout);
// Request tools list via MCP protocol
const listToolsRequest = {
jsonrpc: '2.0',
id: 1,
method: 'tools/list'
};
child.stdin.write(JSON.stringify(listToolsRequest) + '\n');
child.stdout.on('data', (data) => {
output += data.toString();
try {
const lines = output.split('\n').filter(line => line.trim());
for (const line of lines) {
const response = JSON.parse(line);
if (response.result && response.result.tools) {
actualToolsList = response.result.tools.map(t => t.name);
this.log(`Found ${actualToolsList.length} tools in server response`, 'debug');
clearTimeout(timeout);
child.kill();
resolve({
success: true,
actualToolsList,
expectedCount: this.actualTools.length,
foundCount: actualToolsList.length,
missingTools: this.actualTools.filter(name => !actualToolsList.includes(name)),
extraTools: actualToolsList.filter(name => !this.actualTools.includes(name))
});
return;
}
}
} catch (e) {
// Continue parsing
}
});
child.on('close', () => {
clearTimeout(timeout);
});
});
}
analyzeServerLogs(stdout, stderr) {
const analysis = {
serverStarted: false,
toolsRegistered: false,
authenticationMode: 'unknown',
destinationService: 'unknown',
toolCount: 0,
errors: [],
warnings: []
};
const combinedOutput = stdout + stderr;
// Check server startup
if (combinedOutput.includes('SAP MCP Server running on stdio')) {
analysis.serverStarted = true;
}
// Check tools registration
if (combinedOutput.includes('TOTAL TOOLS REGISTERED')) {
analysis.toolsRegistered = true;
const match = combinedOutput.match(/TOTAL TOOLS REGISTERED: (\d+)/);
if (match) {
analysis.toolCount = parseInt(match[1]);
}
}
// Check authentication mode
if (combinedOutput.includes('Authentication will be disabled')) {
analysis.authenticationMode = 'disabled';
} else if (combinedOutput.includes('PRODUCTION authentication')) {
analysis.authenticationMode = 'production';
}
// Check destination service
if (combinedOutput.includes('Could not find service binding of type \'destination\'')) {
analysis.destinationService = 'missing';
} else if (combinedOutput.includes('destination service initialized')) {
analysis.destinationService = 'available';
}
// Extract errors
const errorMatches = combinedOutput.match(/❌[^\\n]+/g);
if (errorMatches) {
analysis.errors = errorMatches.slice(0, 5); // Limit to 5 errors
}
// Extract warnings
const warningMatches = combinedOutput.match(/⚠️[^\\n]+/g);
if (warningMatches) {
analysis.warnings = warningMatches.slice(0, 5); // Limit to 5 warnings
}
return analysis;
}
recordResult(testName, result) {
this.results.tests.push({
name: testName,
success: result.success,
details: result
});
if (result.success) {
this.results.passed++;
this.log(`✅ ${testName}: PASSED`, 'success');
} else {
this.results.failed++;
this.log(`❌ ${testName}: FAILED`, 'error');
}
}
// Generate comprehensive report
generateReport() {
const report = {
summary: {
totalTests: this.results.tests.length,
passed: this.results.passed,
failed: this.results.failed,
successRate: Math.round((this.results.passed / this.results.tests.length) * 100)
},
toolsAnalysis: {
expectedTools: this.actualTools.length,
expectedToolsList: this.actualTools
},
recommendations: []
};
// Add recommendations based on results
if (this.results.failed > 0) {
report.recommendations.push('Fix server startup issues before proceeding with tool testing');
report.recommendations.push('Ensure local environment has minimal SAP configuration');
}
if (this.results.passed === this.results.tests.length) {
report.recommendations.push('Server tool registration is working correctly');
report.recommendations.push('Proceed with comprehensive tool execution testing');
}
return report;
}
}
// Main execution
if (import.meta.url === `file://${process.argv[1]}`) {
const test = new ActualServerToolsTest({ verbose: true });
console.log(chalk.blue.bold('\n🔍 ACTUAL SERVER TOOLS TEST'));
console.log(chalk.gray('============================\n'));
test.runTest().then(results => {
console.log('\n📊 RESULTS SUMMARY:');
console.log(`✅ Passed: ${results.passed}`);
console.log(`❌ Failed: ${results.failed}`);
console.log(`📈 Total Tests: ${results.tests.length}`);
const report = test.generateReport();
console.log(`\n🎯 Success Rate: ${report.summary.successRate}%`);
console.log(`📋 Expected Tools: ${report.toolsAnalysis.expectedTools}`);
if (report.recommendations.length > 0) {
console.log('\n💡 RECOMMENDATIONS:');
report.recommendations.forEach(rec => {
console.log(` • ${rec}`);
});
}
// Show detailed results for failed tests
if (results.failed > 0) {
console.log('\n❌ FAILED TEST DETAILS:');
results.tests.filter(t => !t.success).forEach(test => {
console.log(`\n Test: ${test.name}`);
if (test.details.analysis) {
console.log(` Server Started: ${test.details.analysis.serverStarted}`);
console.log(` Tools Registered: ${test.details.analysis.toolsRegistered}`);
console.log(` Tool Count: ${test.details.analysis.toolCount}`);
console.log(` Auth Mode: ${test.details.analysis.authenticationMode}`);
if (test.details.analysis.errors.length > 0) {
console.log(` Errors: ${test.details.analysis.errors.length}`);
}
}
});
}
process.exit(results.failed > 0 ? 1 : 0);
}).catch(error => {
console.error('\n💥 Test suite crashed:', error.message);
process.exit(1);
});
}
export default ActualServerToolsTest;