import { serve } from '@hono/node-server';
import { Hono } from 'hono';
// import { app as mcpApp } from './index.js'; // Removed static import
import { BecknClient } from './lib/becknClient.js';
import { WebhookManager } from './lib/webhookManager.js';
import sodium from 'libsodium-wrappers';
import axios from 'axios';
async function runIntegrationTest() {
process.env.NODE_ENV = 'test'; // Set env var before dynamic import
const { app: mcpApp } = await import('./index.js');
await sodium.ready;
console.log('🚀 Starting Full Integration Test (Localhost)...');
// Generate valid keys for the client to avoid libsodium errors
const keyPair = sodium.crypto_sign_keypair();
const privateKey = sodium.to_base64(keyPair.privateKey, sodium.base64_variants.ORIGINAL);
const publicKey = sodium.to_base64(keyPair.publicKey, sodium.base64_variants.ORIGINAL);
// 1. Start Mock BPP (The "Provider")
const bppApp = new Hono();
bppApp.post('/search', async (c) => {
console.log('🚖 [BPP] Received Search Request');
const body = await c.req.json();
// Verify Auth Header exists (Basic check)
const auth = c.req.header('Authorization');
if (!auth || !auth.startsWith('Signature')) {
return c.json({ error: 'Unauthorized' }, 401);
}
console.log('🔒 [BPP] Signature Present');
// Simulate Async Callback
setTimeout(async () => {
console.log('📞 [BPP] Sending Callback to BAP...');
try {
await axios.post('http://localhost:3000/on_search', {
context: { ...body.context, action: 'on_search' },
message: {
catalog: {
providers: [{
id: 'driver-1',
descriptor: { name: 'Mock Driver' }
}]
}
}
});
} catch (e) {
console.error('❌ [BPP] Callback Failed', e.message);
}
}, 500);
return c.json({ message: { ack: { status: 'ACK' } } });
});
const bppServer = serve({ fetch: bppApp.fetch, port: 3001 });
console.log('✅ [BPP] Running on port 3001');
// 2. Start MCP Server (The "BAP")
// We need to override the BecknClient in the app to point to localhost:3001
// Since we can't easily inject into the running instance, we'll rely on env vars or
// just instantiate a new client for the test script to drive the flow,
// BUT the MCP server uses its own internal client.
// FIX: We will set process.env.BPP_URI before importing/starting.
const mcpServer = serve({ fetch: mcpApp.fetch, port: 3000 });
console.log('✅ [MCP] Running on port 3000');
// 3. Drive the Test
// We will manually trigger the tool logic or just use the WebhookManager directly
// to simulate what the Tool would do.
// Actually, let's use the Real Tool via the MCP Protocol?
// That's complex to mock the MCP client.
// Simpler: We'll instantiate a BecknClient pointing to our Mock BPP
// and use the WebhookManager to wait.
const client = new BecknClient({
bppUri: 'http://localhost:3001',
bapId: 'test-bap',
bapUri: 'http://localhost:3000',
privateKey: privateKey,
publicKey: publicKey,
uniqueKeyId: 'key1'
});
const webhookManager = WebhookManager.getInstance();
const messageId = 'test-msg-' + Date.now();
console.log('⚡️ [Test] Initiating Search...');
const waitPromise = webhookManager.waitForResponse(messageId);
await client.sendRequest('search', {
context: {
domain: 'mobility',
action: 'search',
message_id: messageId,
bap_uri: 'http://localhost:3000'
},
message: { intent: {} }
});
try {
const response = await waitPromise;
console.log('🎉 [Test] Received Callback Response:', JSON.stringify(response));
if (response.context.message_id === messageId) {
console.log('✅ SUCCESS: Integration Test Passed!');
} else {
console.error('❌ FAILURE: ID Mismatch');
process.exit(1);
}
} catch (e) {
console.error('❌ FAILURE: Timeout or Error', e);
process.exit(1);
} finally {
bppServer.close();
mcpServer.close();
process.exit(0);
}
}
runIntegrationTest().catch(console.error);