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

97 lines
3.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 引擎工具回调的端到端用例(任务 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', databasePath: ':memory:', engine: 'fake' });
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();
}
});