aps-agent/scripts/internal-tool-call.test.mjs

97 lines
3.9 KiB
JavaScript
Raw Normal View History

/**
* 引擎工具回调的端到端用例(任务 A2.5)。
*
* 路径:HTTP 入口(/internal/tool-call)→ 应用服务 → Core.invoke → 假 Provider → 回执与执行记录。
* 同时验证鉴权:只允许 loopback + 内部令牌。
*/
import assert from 'node:assert/strict';
import { test } from 'node:test';
async function startWithProvider() {
process.env.APS_DEV_PROVIDER = '1';
const { startServer } = await import('../server/dist/index.js');
const server = await startServer({ host: '127.0.0.1', port: 0, version: 'test' });
const created = await fetch(`${server.url}/v1/sessions`, {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ scope: 'project', projectId: 'p-default', title: '工具回调会话' }),
});
const session = (await created.json()).session;
return { server, session };
}
const callTool = (server, body, token) =>
fetch(`${server.url}/internal/tool-call`, {
method: 'POST',
headers: { 'content-type': 'application/json', ...(token === undefined ? {} : { 'x-aps-internal-token': token }) },
body: JSON.stringify(body),
});
test('HTTP → 应用服务 → Core → 假 Provider 的完整链路', async () => {
const { server, session } = await startWithProvider();
try {
const response = await callTool(
server,
{ sessionId: session.sessionId, capabilityId: 'fake.echo', params: { text: '端到端' } },
server.internalToken,
);
assert.equal(response.status, 200);
const receipt = await response.json();
assert.equal(receipt.status, 'succeeded');
assert.deepEqual(receipt.result, { echo: '端到端' });
// 执行记录:身份取自会话归属者,参数只留摘要
const record = server.service.core.executions.get(receipt.executionId);
assert.ok(record);
assert.equal(record.sessionId, session.sessionId);
assert.equal(record.projectId, 'p-default');
assert.equal(record.subjectUserId, 'dev-user', '身份应取自会话归属者,而不是调用方自带');
assert.match(record.paramsDigest, /^sha256:/);
const lookup = await fetch(`${server.url}/v1/executions/${receipt.executionId}`);
assert.equal(lookup.status, 200);
assert.equal((await lookup.json()).status, 'succeeded');
} finally {
delete process.env.APS_DEV_PROVIDER;
await server.close();
}
});
test('内部令牌缺失或错误时拒绝,且不产生执行记录', async () => {
const { server, session } = await startWithProvider();
try {
const body = { sessionId: session.sessionId, capabilityId: 'fake.echo', params: { text: 'x' } };
const missing = await callTool(server, body);
assert.equal(missing.status, 401);
assert.equal((await missing.json()).error.code, 'unauthorized');
const wrong = await callTool(server, body, 'not-the-token');
assert.equal(wrong.status, 401);
const before = server.service.core.executions.list().length;
const bad = await callTool(server, { sessionId: session.sessionId }, server.internalToken);
assert.equal(bad.status, 400, '缺 capabilityId 应 400');
assert.equal(server.service.core.executions.list().length, before, '被拒的调用不应产生执行记录');
} finally {
delete process.env.APS_DEV_PROVIDER;
await server.close();
}
});
test('未知能力经回调返回失败回执(不是 HTTP 错误)', async () => {
const { server, session } = await startWithProvider();
try {
const response = await callTool(
server,
{ sessionId: session.sessionId, capabilityId: 'fake.__nope__', params: {} },
server.internalToken,
);
assert.equal(response.status, 200, '能力不存在属于业务失败,不是协议错误');
const receipt = await response.json();
assert.equal(receipt.status, 'failed');
assert.equal(receipt.errorKind, 'invalid_argument');
} finally {
delete process.env.APS_DEV_PROVIDER;
await server.close();
}
});