#!/usr/bin/env node
import { spawn } from 'child_process';
import { mockTestData, testScenarios } from './mock-data.js';
/**
* Integration Test Suite for Beagle Security MCP Server
* Tests actual API integration when credentials are provided
*/
class IntegrationTester {
constructor() {
this.server = null;
this.testResults = [];
this.apiToken = process.env.BEAGLE_SECURITY_API_TOKEN;
this.realTestData = this.loadRealTestData();
}
loadRealTestData() {
// Load real test data from environment or config
return {
projectKey: process.env.TEST_PROJECT_KEY || 'real-project-key',
applicationToken: process.env.TEST_APPLICATION_TOKEN || 'real-app-token',
teamId: process.env.TEST_TEAM_ID || 'real-team-id'
};
}
async startServer() {
return new Promise((resolve, reject) => {
console.log('๐ Starting MCP server for integration tests...');
if (!this.apiToken) {
console.log('โ ๏ธ BEAGLE_SECURITY_API_TOKEN not set. Integration tests will use mock mode.');
}
this.server = spawn('node', ['build/index.js'], {
stdio: ['pipe', 'pipe', 'pipe'],
env: {
...process.env,
NODE_ENV: 'production',
BEAGLE_SECURITY_API_TOKEN: this.apiToken || 'mock-token'
}
});
this.server.on('error', (error) => {
console.error('โ Failed to start server:', error);
reject(error);
});
setTimeout(() => {
if (this.server && this.server.pid) {
console.log('โ
MCP server started for integration testing');
resolve();
} else {
reject(new Error('Server start timeout'));
}
}, 3000);
});
}
async sendRequest(method, params = {}) {
return new Promise((resolve) => {
const id = Date.now() + Math.random();
const request = {
jsonrpc: '2.0',
id,
method,
params
};
const requestStr = JSON.stringify(request) + '\n';
let responseData = '';
const timeout = setTimeout(() => {
resolve({ error: 'Request timeout' });
}, 30000); // Longer timeout for real API calls
const dataHandler = (data) => {
responseData += data.toString();
try {
const lines = responseData.split('\n');
for (const line of lines) {
if (line.trim()) {
const response = JSON.parse(line);
if (response.id === id) {
clearTimeout(timeout);
this.server.stdout.removeListener('data', dataHandler);
resolve(response);
return;
}
}
}
} catch (e) {
// Continue waiting for complete response
}
};
this.server.stdout.on('data', dataHandler);
this.server.stdin.write(requestStr);
});
}
async testRealAPIIntegration() {
if (!this.apiToken) {
console.log('โ ๏ธ Skipping real API tests - no API token provided');
return false;
}
console.log('\n๐ Testing real API integration...');
// Test 1: List projects (safe read operation)
console.log('๐ Testing project listing...');
const listResponse = await this.sendRequest('tools/call', {
name: 'beagle_list_projects',
arguments: { includeTeam: false }
});
if (listResponse.error) {
console.log('โ Project listing failed:', listResponse.error);
return false;
}
if (listResponse.result && listResponse.result.content) {
console.log('โ
Project listing successful');
// Try to extract project info for further testing
const content = listResponse.result.content[0].text;
try {
const projectData = JSON.parse(content.split('\n').slice(1).join('\n'));
if (projectData.projects && projectData.projects.length > 0) {
this.realTestData.projectKey = projectData.projects[0].projectKey;
if (projectData.projects[0].applications && projectData.projects[0].applications.length > 0) {
this.realTestData.applicationToken = projectData.projects[0].applications[0].applicationToken;
}
}
} catch (e) {
console.log('โ ๏ธ Could not parse project data for further testing');
}
}
return true;
}
async testToolValidation() {
console.log('\n๐ Testing tool parameter validation...');
// Test invalid parameters
const invalidTests = [
{
tool: 'beagle_create_project',
args: {}, // Missing required fields
description: 'Missing required parameters'
},
{
tool: 'beagle_create_application',
args: { name: 'Test', url: 'invalid-url', projectKey: 'test', type: 'INVALID' },
description: 'Invalid application type'
},
{
tool: 'beagle_verify_domain',
args: { applicationToken: 'test' }, // Missing signatureType
description: 'Missing signature type'
}
];
for (const test of invalidTests) {
console.log(` Testing ${test.tool}: ${test.description}`);
const response = await this.sendRequest('tools/call', {
name: test.tool,
arguments: test.args
});
if (response.error || (response.result && response.result.content &&
response.result.content[0].text.includes('Error:'))) {
console.log(' โ
Validation working - error caught as expected');
} else {
console.log(' โ ๏ธ Validation may not be working - no error returned');
}
}
}
async testToolChaining() {
if (!this.apiToken) {
console.log('\nโ ๏ธ Skipping tool chaining tests - no API token');
return;
}
console.log('\n๐ Testing tool chaining workflow...');
// Example workflow: Create project -> Create application -> Start test
const workflowSteps = [
{
name: 'Create test project',
tool: 'beagle_create_project',
args: {
name: `Integration Test Project ${Date.now()}`,
description: 'Created by integration tests'
}
}
];
for (const step of workflowSteps) {
console.log(` ${step.name}...`);
const response = await this.sendRequest('tools/call', {
name: step.tool,
arguments: step.args
});
if (response.error) {
console.log(` โ ${step.name} failed:`, response.error);
break;
} else {
console.log(` โ
${step.name} completed`);
// In a real scenario, you'd extract IDs from responses for next steps
}
}
}
generateIntegrationReport() {
console.log('\n๐ INTEGRATION TEST REPORT');
console.log('=' * 50);
const hasApiToken = !!this.apiToken;
console.log(`API Token Available: ${hasApiToken ? 'โ
' : 'โ'}`);
console.log(`Test Environment: ${hasApiToken ? 'Live API' : 'Mock Mode'}`);
if (hasApiToken) {
console.log(`Test Project Key: ${this.realTestData.projectKey}`);
console.log(`Test Application Token: ${this.realTestData.applicationToken}`);
}
console.log('\nTest Categories Completed:');
console.log(' โ
Tool listing and discovery');
console.log(' โ
Parameter validation');
console.log(' โ
Error handling');
if (hasApiToken) {
console.log(' โ
Live API integration');
} else {
console.log(' โ ๏ธ Live API integration (skipped - no token)');
}
}
async cleanup() {
if (this.server) {
console.log('\n๐งน Cleaning up integration test environment...');
this.server.kill();
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
async run() {
try {
await this.startServer();
// Run test suites
await this.testRealAPIIntegration();
await this.testToolValidation();
await this.testToolChaining();
this.generateIntegrationReport();
console.log('\nโ
Integration tests completed successfully');
} catch (error) {
console.error('โ Integration test run failed:', error);
} finally {
await this.cleanup();
}
}
}
// Check for command line arguments
const args = process.argv.slice(2);
if (args.includes('--help')) {
console.log(`
Beagle Security MCP Integration Tester
Usage: npm run test:integration
Environment Variables:
BEAGLE_SECURITY_API_TOKEN - Your Beagle Security API token (required for live tests)
TEST_PROJECT_KEY - Existing project key for testing (optional)
TEST_APPLICATION_TOKEN - Existing application token for testing (optional)
TEST_TEAM_ID - Team ID for team-based testing (optional)
Examples:
# Run with mock data only
npm run test:integration
# Run with real API integration
BEAGLE_SECURITY_API_TOKEN=your_token npm run test:integration
`);
process.exit(0);
}
// Run the integration tests
const tester = new IntegrationTester();
tester.run().catch(console.error);