test-atxp-flow.js•15.8 kB
#!/usr/bin/env node
/**
* ATXP Flow Comprehensive Test Suite
* Tests the new ATXP server with OAuth2 authentication and crypto payments
* Validates the complete payment flow and tool execution
*/
const https = require('https');
const http = require('http');
// Test configuration for ATXP server
const ATXP_CONFIG = {
serverUrl: 'http://localhost:5001',
endpoints: {
toolsList: '/',
toolsCall: '/',
oauth: '/auth/oauth',
payment: '/payment'
}
};
// ANSI colors for output
const colors = {
green: '\x1b[32m',
red: '\x1b[31m',
yellow: '\x1b[33m',
blue: '\x1b[34m',
purple: '\x1b[35m',
cyan: '\x1b[36m',
reset: '\x1b[0m',
bold: '\x1b[1m'
};
class ATXPFlowTester {
constructor() {
this.results = {
passed: 0,
failed: 0,
errors: [],
warnings: []
};
this.authToken = null;
this.walletAddress = null;
}
log(message, color = 'reset') {
console.log(`${colors[color]}${message}${colors.reset}`);
}
success(test) {
this.results.passed++;
this.log(`✅ ${test}`, 'green');
}
fail(test, error) {
this.results.failed++;
this.results.errors.push({ test, error });
this.log(`❌ ${test}: ${error}`, 'red');
}
warning(test, message) {
this.results.warnings.push({ test, message });
this.log(`⚠️ ${test}: ${message}`, 'yellow');
}
info(message) {
this.log(`ℹ️ ${message}`, 'blue');
}
async makeATXPRequest(method, data = null, headers = {}) {
return new Promise((resolve, reject) => {
const url = new URL(ATXP_CONFIG.serverUrl);
const defaultHeaders = {
'Content-Type': 'application/json',
...headers
};
if (this.authToken) {
defaultHeaders['Authorization'] = `Bearer ${this.authToken}`;
}
const options = {
hostname: url.hostname,
port: url.port,
path: '/',
method: 'POST',
headers: defaultHeaders
};
const req = http.request(options, (res) => {
let body = '';
res.on('data', (chunk) => body += chunk);
res.on('end', () => {
try {
const parsed = JSON.parse(body);
resolve({ status: res.statusCode, data: parsed, headers: res.headers });
} catch (e) {
resolve({ status: res.statusCode, data: body, headers: res.headers });
}
});
});
req.on('error', reject);
if (data) {
req.write(JSON.stringify(data));
}
req.end();
});
}
async testServerAvailability() {
this.log('\n🌐 Testing ATXP Server Availability', 'bold');
this.log('==================================', 'bold');
try {
const response = await this.makeATXPRequest('POST');
if (response.status === 200 || response.status === 400) {
this.success('ATXP server is running and reachable');
return true;
} else {
this.fail('Server availability', `HTTP ${response.status}`);
return false;
}
} catch (error) {
this.fail('Server availability', error.message);
return false;
}
}
async testToolsList() {
this.log('\n🔧 Testing Tools List (No Auth Required)', 'bold');
this.log('========================================', 'bold');
try {
const payload = {
jsonrpc: "2.0",
method: "tools/list",
id: "test-tools-list"
};
const response = await this.makeATXPRequest('POST', payload);
if (response.status === 200 && response.data.result) {
const tools = response.data.result.tools || [];
this.success(`Tools list retrieved - Found ${tools.length} tools`);
// Verify expected tools exist
const expectedTools = ['create_agent', 'list_agents', 'get_pricing', 'prompt_agent'];
const foundTools = tools.map(t => t.name);
expectedTools.forEach(tool => {
if (foundTools.includes(tool)) {
this.log(` ✓ Found tool: ${tool}`, 'green');
} else {
this.warning('Tools validation', `Missing expected tool: ${tool}`);
}
});
return true;
} else {
this.fail('Tools list', `Unexpected response: ${JSON.stringify(response.data)}`);
return false;
}
} catch (error) {
this.fail('Tools list', error.message);
return false;
}
}
async testOAuth2Flow() {
this.log('\n🔐 Testing OAuth2 Authentication Flow', 'bold');
this.log('====================================', 'bold');
this.info('Note: OAuth2 requires interactive browser flow');
this.info('In production, users would:');
this.log(' 1. Visit OAuth2 authorization URL', 'cyan');
this.log(' 2. Connect their wallet (MetaMask/WalletConnect)', 'cyan');
this.log(' 3. Authorize the application', 'cyan');
this.log(' 4. Receive access token', 'cyan');
// Simulate getting a token (in real flow this comes from OAuth2)
this.authToken = 'mock_atxp_token_' + Math.random().toString(36);
this.warning('OAuth2 simulation', 'Using mock token for testing - real OAuth2 requires browser');
return true;
}
async testPaymentAuthentication() {
this.log('\n💳 Testing Payment Authentication', 'bold');
this.log('=================================', 'bold');
// Test tool call that requires payment (should fail without proper ATXP auth)
try {
const payload = {
jsonrpc: "2.0",
method: "tools/call",
params: {
name: "list_agents",
arguments: {
apiKey: process.env.MOLUABI_API_KEY || 'mab_test_key'
}
},
id: "test-payment-auth"
};
const response = await this.makeATXPRequest('POST', payload);
if (response.status === 401 || response.status === 403) {
this.success('Payment authentication enforced - Unauthorized access blocked');
} else if (response.status === 402) {
this.success('Payment required response - ATXP payment system active');
} else if (response.data.error && response.data.error.code === -32001) {
this.success('Payment required via MCP error - ATXP integration working');
} else {
this.warning('Payment auth', `Unexpected response: HTTP ${response.status}`);
}
} catch (error) {
this.fail('Payment authentication', error.message);
}
}
async testCryptoPaymentFlow() {
this.log('\n💰 Testing Crypto Payment Flow', 'bold');
this.log('==============================', 'bold');
this.info('Crypto payment flow simulation:');
this.log(' 1. User connects wallet (MetaMask/WalletConnect)', 'cyan');
this.log(' 2. Tool execution requires payment (e.g., $0.001 USDC)', 'cyan');
this.log(' 3. User approves transaction in wallet', 'cyan');
this.log(' 4. Payment confirmed on blockchain', 'cyan');
this.log(' 5. Tool executes successfully', 'cyan');
// Simulate wallet connection
this.walletAddress = '0x' + Math.random().toString(16).substring(2, 42).padStart(40, '0');
this.log(` 💳 Simulated wallet: ${this.walletAddress}`, 'purple');
// Test payment pricing information
try {
const pricingPayload = {
jsonrpc: "2.0",
method: "tools/call",
params: {
name: "get_pricing",
arguments: {
apiKey: process.env.MOLUABI_API_KEY || 'mab_test_key'
}
},
id: "test-pricing"
};
this.info('Testing pricing tool (should show payment required)...');
const response = await this.makeATXPRequest('POST', pricingPayload);
if (response.status === 402 || (response.data.error && response.data.error.code === -32001)) {
this.success('Pricing tool correctly requires payment');
} else {
this.warning('Payment flow', 'Pricing tool should require payment');
}
} catch (error) {
this.fail('Crypto payment flow', error.message);
}
}
async testATXPIntegration() {
this.log('\n🔗 Testing ATXP Integration Features', 'bold');
this.log('===================================', 'bold');
// Test ATXP-specific headers and features
const testCases = [
{
name: 'ATXP version compatibility',
check: () => {
this.log(' 📦 @atxp/server version: Latest', 'cyan');
this.success('ATXP server package integrated');
}
},
{
name: 'Payment destination configured',
check: () => {
const destination = process.env.PAYMENT_DESTINATION;
if (destination) {
this.log(` 💳 Payment destination: ${destination.substring(0, 8)}...`, 'cyan');
this.success('Payment destination configured');
} else {
this.warning('Configuration', 'PAYMENT_DESTINATION not set');
}
}
},
{
name: 'ATXP client token',
check: () => {
const token = process.env.ATXP_AUTH_CLIENT_TOKEN;
if (token) {
this.log(' 🔑 ATXP client token: Configured', 'cyan');
this.success('ATXP authentication token configured');
} else {
this.warning('Configuration', 'ATXP_AUTH_CLIENT_TOKEN not set');
}
}
}
];
testCases.forEach(test => test.check());
}
async testToolExecution() {
this.log('\n⚙️ Testing Tool Execution with Payment', 'bold');
this.log('======================================', 'bold');
const tools = [
{
name: 'list_agents',
args: { apiKey: process.env.MOLUABI_API_KEY || 'mab_test_key' },
expectedPrice: 0.001
},
{
name: 'get_pricing',
args: { apiKey: process.env.MOLUABI_API_KEY || 'mab_test_key' },
expectedPrice: 0.001
},
{
name: 'create_agent',
args: {
apiKey: process.env.MOLUABI_API_KEY || 'mab_test_key',
name: 'ATXP Test Agent',
description: 'Test agent for ATXP flow validation'
},
expectedPrice: 0.05
}
];
for (const tool of tools) {
this.info(`Testing ${tool.name} (Expected: $${tool.expectedPrice} USDC)`);
try {
const payload = {
jsonrpc: "2.0",
method: "tools/call",
params: {
name: tool.name,
arguments: tool.args
},
id: `test-${tool.name}`
};
const response = await this.makeATXPRequest('POST', payload);
if (response.status === 402 || (response.data.error && response.data.error.code === -32001)) {
this.success(`${tool.name} correctly requires payment ($${tool.expectedPrice})`);
} else if (response.status === 200 && response.data.result) {
this.warning(`${tool.name}`, 'Tool executed without payment - check ATXP configuration');
} else {
this.warning(`${tool.name}`, `Unexpected response: ${response.status}`);
}
} catch (error) {
this.fail(`${tool.name} execution`, error.message);
}
}
}
async testErrorHandling() {
this.log('\n🚫 Testing Error Handling', 'bold');
this.log('=========================', 'bold');
const errorTests = [
{
name: 'Invalid tool name',
payload: {
jsonrpc: "2.0",
method: "tools/call",
params: { name: "nonexistent_tool", arguments: {} },
id: "error-test-1"
}
},
{
name: 'Malformed JSON-RPC',
payload: {
method: "tools/call", // Missing jsonrpc and id
params: { name: "list_agents", arguments: {} }
}
},
{
name: 'Missing arguments',
payload: {
jsonrpc: "2.0",
method: "tools/call",
params: { name: "create_agent", arguments: {} }, // Missing required fields
id: "error-test-3"
}
}
];
for (const test of errorTests) {
try {
const response = await this.makeATXPRequest('POST', test.payload);
if (response.data.error || response.status >= 400) {
this.success(`${test.name} - Error properly handled`);
} else {
this.warning(test.name, 'Should have returned an error');
}
} catch (error) {
this.success(`${test.name} - Network error properly handled`);
}
}
}
printSummary() {
this.log('\n📊 ATXP Flow Test Results', 'bold');
this.log('=========================', 'bold');
const total = this.results.passed + this.results.failed;
this.log(`\nTotal Tests: ${total}`, 'cyan');
this.log(`Passed: ${this.results.passed}`, 'green');
this.log(`Failed: ${this.results.failed}`, this.results.failed > 0 ? 'red' : 'green');
this.log(`Warnings: ${this.results.warnings.length}`, 'yellow');
if (this.results.errors.length > 0) {
this.log('\n❌ Failed Tests:', 'red');
this.results.errors.forEach(error => {
this.log(` • ${error.test}: ${error.error}`, 'red');
});
}
if (this.results.warnings.length > 0) {
this.log('\n⚠️ Warnings:', 'yellow');
this.results.warnings.forEach(warning => {
this.log(` • ${warning.test}: ${warning.message}`, 'yellow');
});
}
this.log('\n🎯 ATXP Flow Status:', 'bold');
if (this.results.failed === 0) {
this.log('✅ ATXP server is ready for production use!', 'green');
} else {
this.log('⚠️ ATXP server needs attention before production.', 'yellow');
}
this.log('\n💡 Next Steps for Agent Team:', 'bold');
this.log(' 1. Implement ATXP SDK integration in your client', 'cyan');
this.log(' 2. Set up OAuth2 flow with wallet connection', 'cyan');
this.log(' 3. Test payment flow with real crypto transactions', 'cyan');
this.log(' 4. Switch server URL to port 5001 for ATXP mode', 'cyan');
}
async runAllTests() {
this.log('🧪 ATXP Flow Comprehensive Test Suite', 'bold');
this.log('====================================', 'bold');
this.log('Testing OAuth2 authentication and crypto payment integration\n');
// Environment check
const requiredEnvVars = ['MOLUABI_API_KEY', 'PAYMENT_DESTINATION', 'ATXP_AUTH_CLIENT_TOKEN'];
requiredEnvVars.forEach(envVar => {
if (process.env[envVar]) {
this.log(`✅ ${envVar} configured`, 'green');
} else {
this.log(`⚠️ ${envVar} not set`, 'yellow');
}
});
// Run test suites
const serverAvailable = await this.testServerAvailability();
if (!serverAvailable) {
this.log('\n❌ ATXP server not available - stopping tests', 'red');
this.printSummary();
return;
}
await this.testToolsList();
await this.testOAuth2Flow();
await this.testPaymentAuthentication();
await this.testCryptoPaymentFlow();
await this.testATXPIntegration();
await this.testToolExecution();
await this.testErrorHandling();
this.printSummary();
}
}
// Usage instructions
if (require.main === module) {
console.log('💡 ATXP Flow Test Instructions:');
console.log(' 1. Make sure ATXP server is running: npm run start:atxp (port 5001)');
console.log(' 2. Set environment variables:');
console.log(' - MOLUABI_API_KEY=mab_your_key');
console.log(' - PAYMENT_DESTINATION=your_wallet_address');
console.log(' - ATXP_AUTH_CLIENT_TOKEN=your_client_token');
console.log(' 3. Run: node test-atxp-flow.js\n');
const tester = new ATXPFlowTester();
tester.runAllTests().catch(console.error);
}
module.exports = ATXPFlowTester;