Files
crm-ai/bridge/test/server.test.js
T
2026-10-23 10:35:28 +08:00

152 lines
4.6 KiB
JavaScript

'use strict';
const test = require('node:test');
const assert = require('node:assert/strict');
const { createServer } = require('../src/server');
const baseCfg = {
port: 0,
toolsSharedKey: 'test-key',
toolsPublicBaseUrl: 'http://host.docker.internal:4010',
devBypassVerification: false,
twenty: {},
};
function fakeTools() {
return [
{
name: 'search_products',
description: 'search',
parameters: { type: 'object', properties: { query: { type: 'string' } } },
requiresVerification: false,
handler: async (args, ctx) => ({ echo: args.query || null, verified: ctx.verified }),
},
{
name: 'search_orders',
description: 'orders',
parameters: { type: 'object', properties: {} },
requiresVerification: true,
handler: async () => ({ orders: [] }),
},
];
}
async function startServer(tools = fakeTools(), cfg = baseCfg) {
const server = createServer({ config: cfg, tools });
await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve));
const { port } = server.address();
return {
base: `http://127.0.0.1:${port}`,
close: () => new Promise((resolve) => server.close(resolve)),
};
}
test('GET /healthz is public and reports tool names', async () => {
const s = await startServer();
try {
const res = await fetch(`${s.base}/healthz`);
assert.equal(res.status, 200);
const body = await res.json();
assert.equal(body.ok, true);
assert.deepEqual(body.tools, ['search_products', 'search_orders']);
} finally {
await s.close();
}
});
test('tools endpoints reject missing/wrong key', async () => {
const s = await startServer();
try {
const missing = await fetch(`${s.base}/tools/manifest`);
assert.equal(missing.status, 401);
const wrong = await fetch(`${s.base}/tools/manifest`, { headers: { 'x-tools-key': 'nope' } });
assert.equal(wrong.status, 401);
} finally {
await s.close();
}
});
test('manifest exposes URL, schema and verification flag', async () => {
const s = await startServer();
try {
const res = await fetch(`${s.base}/tools/manifest`, { headers: { 'x-tools-key': 'test-key' } });
const body = await res.json();
assert.equal(res.status, 200);
const orders = body.tools.find((t) => t.name === 'search_orders');
assert.equal(orders.url, 'http://host.docker.internal:4010/tools/search_orders');
assert.equal(orders.requires_verification, true);
assert.equal(body.tools[0].auth.headers[0].key, 'X-Tools-Key');
} finally {
await s.close();
}
});
test('verification is enforced on the server, not only in LibreDesk', async () => {
const s = await startServer();
try {
const res = await fetch(`${s.base}/tools/search_orders`, {
method: 'POST',
headers: { 'x-tools-key': 'test-key', 'content-type': 'application/json' },
body: '{}',
});
assert.equal(res.status, 403);
const body = await res.json();
assert.equal(body.error, 'verification_required');
} finally {
await s.close();
}
});
test('verified header is forwarded to handlers and result is returned', async () => {
const s = await startServer();
try {
const res = await fetch(`${s.base}/tools/search_products`, {
method: 'POST',
headers: {
'x-tools-key': 'test-key',
'content-type': 'application/json',
'x-libredesk-contact-email': 'A@B.com',
'x-libredesk-contact-verified': 'true',
},
body: JSON.stringify({ query: 'hoops' }),
});
const body = await res.json();
assert.equal(res.status, 200);
assert.equal(body.ok, true);
assert.equal(body.tool, 'search_products');
assert.equal(body.echo, 'hoops');
assert.equal(body.verified, true);
} finally {
await s.close();
}
});
test('invalid JSON is a 400, unknown tool is a 404', async () => {
const s = await startServer();
try {
const bad = await fetch(`${s.base}/tools/search_products`, {
method: 'POST',
headers: { 'x-tools-key': 'test-key', 'content-type': 'application/json' },
body: '{not-json',
});
assert.equal(bad.status, 400);
const unknown = await fetch(`${s.base}/tools/nope`, {
method: 'POST',
headers: { 'x-tools-key': 'test-key' },
});
assert.equal(unknown.status, 404);
} finally {
await s.close();
}
});
test('missing TOOLS_SHARED_KEY fails closed with 503', async () => {
const s = await startServer(fakeTools(), { ...baseCfg, toolsSharedKey: '' });
try {
const res = await fetch(`${s.base}/tools/manifest`, { headers: { 'x-tools-key': '' } });
assert.equal(res.status, 503);
} finally {
await s.close();
}
});