refactor(core,server): 统一模块职责并消除类型重复
Core 模块职责: - 添加 inferPermissionType() 权限类型推断函数 - 添加 partToApiFormat()/partsToApiFormat() API 格式转换函数 - 添加 ApiPart/ApiTextPart/ApiToolPart/ApiReasoningPart 类型 - 统一导出 toolRegistry 供 Server 使用 Server 模块职责: - 重命名 PermissionRequestContext 为 PermissionDisplayContext - 移除本地 toolRegistry,直接使用 Core 的注册表 - 使用 Core 的 inferPermissionType 替代本地实现 - 使用 Core 的 partsToApiFormat 替代手动转换 文档更新: - 在 gui-server-client.md 添加第11章「模块职责边界」 - 明确 Core 和 Server 的职责划分
This commit is contained in:
@@ -2,11 +2,13 @@
|
||||
* Tools Route 测试
|
||||
*
|
||||
* 测试工具管理 REST API 端点
|
||||
* 注意:现在使用 Core 的 toolRegistry,工具已预先注册
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
||||
import { Hono } from 'hono';
|
||||
import { toolsRouter, registerTool, getRegisteredTools } from '../../../src/routes/tools.js';
|
||||
import { toolsRouter } from '../../../src/routes/tools.js';
|
||||
import { toolRegistry } from '@ai-assistant/core';
|
||||
|
||||
// Create test app
|
||||
const app = new Hono();
|
||||
@@ -14,71 +16,64 @@ app.route('/tools', toolsRouter);
|
||||
|
||||
describe('Tools Route', () => {
|
||||
beforeEach(() => {
|
||||
// Clear any previously registered tools
|
||||
// Note: Since the toolRegistry is a module-level Map, we test what's already registered
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('registerTool / getRegisteredTools - 工具注册', () => {
|
||||
it('注册工具', () => {
|
||||
registerTool({
|
||||
name: 'test-tool',
|
||||
description: 'A test tool',
|
||||
parameters: {},
|
||||
});
|
||||
|
||||
const tools = getRegisteredTools();
|
||||
expect(tools.some((t) => t.name === 'test-tool')).toBe(true);
|
||||
});
|
||||
|
||||
it('获取所有已注册工具', () => {
|
||||
const tools = getRegisteredTools();
|
||||
expect(Array.isArray(tools)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /tools - 列出所有工具', () => {
|
||||
it('返回工具列表', async () => {
|
||||
// Register a tool first
|
||||
registerTool({
|
||||
name: 'list-test-tool',
|
||||
description: 'Tool for list test',
|
||||
parameters: {},
|
||||
});
|
||||
|
||||
const res = await app.request('/tools');
|
||||
const json = await res.json();
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(json.success).toBe(true);
|
||||
expect(Array.isArray(json.data)).toBe(true);
|
||||
// Core toolRegistry 已注册工具
|
||||
expect(json.data.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('返回数组类型数据', async () => {
|
||||
it('工具包含必要字段', async () => {
|
||||
const res = await app.request('/tools');
|
||||
const json = await res.json();
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(json.success).toBe(true);
|
||||
expect(Array.isArray(json.data)).toBe(true);
|
||||
// 检查第一个工具的字段
|
||||
if (json.data.length > 0) {
|
||||
const tool = json.data[0];
|
||||
expect(tool).toHaveProperty('name');
|
||||
expect(tool).toHaveProperty('description');
|
||||
expect(tool).toHaveProperty('parameters');
|
||||
// 不应该包含 execute 函数
|
||||
expect(tool).not.toHaveProperty('execute');
|
||||
}
|
||||
});
|
||||
|
||||
it('返回的工具数量与 Core Registry 一致', async () => {
|
||||
const res = await app.request('/tools');
|
||||
const json = await res.json();
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(json.data.length).toBe(toolRegistry.getAllTools().length);
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /tools/:name - 获取单个工具', () => {
|
||||
it('返回存在的工具', async () => {
|
||||
registerTool({
|
||||
name: 'get-test-tool',
|
||||
description: 'Tool for get test',
|
||||
parameters: { path: { type: 'string' } },
|
||||
});
|
||||
|
||||
const res = await app.request('/tools/get-test-tool');
|
||||
it('返回存在的工具 (bash)', async () => {
|
||||
const res = await app.request('/tools/bash');
|
||||
const json = await res.json();
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(json.success).toBe(true);
|
||||
expect(json.data.name).toBe('get-test-tool');
|
||||
expect(json.data.description).toBe('Tool for get test');
|
||||
expect(json.data.name).toBe('bash');
|
||||
expect(json.data.description).toBeDefined();
|
||||
});
|
||||
|
||||
it('返回存在的工具 (read_file)', async () => {
|
||||
const res = await app.request('/tools/read_file');
|
||||
const json = await res.json();
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(json.success).toBe(true);
|
||||
expect(json.data.name).toBe('read_file');
|
||||
});
|
||||
|
||||
it('工具不存在返回 404', async () => {
|
||||
@@ -92,24 +87,20 @@ describe('Tools Route', () => {
|
||||
});
|
||||
|
||||
describe('POST /tools/:name/execute - 执行工具', () => {
|
||||
it('执行存在的工具(占位实现)', async () => {
|
||||
registerTool({
|
||||
name: 'execute-test-tool',
|
||||
description: 'Tool for execute test',
|
||||
parameters: {},
|
||||
});
|
||||
|
||||
const res = await app.request('/tools/execute-test-tool/execute', {
|
||||
it('存在的工具返回占位响应', async () => {
|
||||
const res = await app.request('/tools/bash/execute', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ params: { test: 'value' } }),
|
||||
body: JSON.stringify({ params: { command: 'echo test' } }),
|
||||
});
|
||||
const json = await res.json();
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(json.success).toBe(true);
|
||||
expect(json.data.tool).toBe('execute-test-tool');
|
||||
expect(json.data.params).toEqual({ test: 'value' });
|
||||
expect(json.data.tool).toBe('bash');
|
||||
expect(json.data.params).toEqual({ command: 'echo test' });
|
||||
// 当前是占位实现
|
||||
expect(json.data.message).toContain('not yet implemented');
|
||||
});
|
||||
|
||||
it('工具不存在返回 404', async () => {
|
||||
@@ -126,13 +117,7 @@ describe('Tools Route', () => {
|
||||
});
|
||||
|
||||
it('无 params 时使用空对象', async () => {
|
||||
registerTool({
|
||||
name: 'no-params-tool',
|
||||
description: 'Tool without params',
|
||||
parameters: {},
|
||||
});
|
||||
|
||||
const res = await app.request('/tools/no-params-tool/execute', {
|
||||
const res = await app.request('/tools/bash/execute', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({}),
|
||||
@@ -144,13 +129,7 @@ describe('Tools Route', () => {
|
||||
});
|
||||
|
||||
it('无效 JSON 返回 500', async () => {
|
||||
registerTool({
|
||||
name: 'json-error-tool',
|
||||
description: 'Tool for JSON error test',
|
||||
parameters: {},
|
||||
});
|
||||
|
||||
const res = await app.request('/tools/json-error-tool/execute', {
|
||||
const res = await app.request('/tools/bash/execute', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: 'invalid json',
|
||||
|
||||
Reference in New Issue
Block a user