/** * ToolRegistry 单元测试(P1-14 测试基线) * 覆盖:truncateResult 截断、未知工具错误、工具超时 */ import { describe, it, expect } from 'vitest'; import { ToolRegistry } from '../registry'; import type { IMetonaTool, ToolExecutionContext } from '../../types/metona-tool'; function createContext(overrides?: Partial): ToolExecutionContext { return { sessionId: 'test', workspacePath: process.cwd(), iteration: 1, requestId: 'req_test', ...overrides, }; } describe('ToolRegistry.truncateResult', () => { const registry = new ToolRegistry(); // 访问私有方法 const truncate = (result: unknown) => (registry as unknown as { truncateResult: (r: unknown) => unknown }).truncateResult(result); it('小结果原样返回', () => { const small = { data: 'x'.repeat(100) }; expect(truncate(small)).toBe(small); }); it('大字符串结果被截断并附加 _truncated 标记', () => { const big = 'a'.repeat(500_000); const truncated = truncate(big) as { _preview: string; _original_size: number; _truncated: boolean }; expect(truncated._truncated).toBe(true); expect(truncated._original_size).toBe(500_000); expect(truncated._preview.length).toBeLessThan(big.length); }); it('大对象结果被截断', () => { const bigObj = { content: 'a'.repeat(400_000), extra: 'b'.repeat(200_000) }; const truncated = truncate(bigObj) as { _truncated: boolean }; expect(truncated._truncated).toBe(true); }); it('view_image 的 dataUrl 白名单不截断(原对象引用返回)', () => { const obj = { dataUrl: `data:image/png;base64,${'a'.repeat(200_000)}` }; expect(truncate(obj)).toBe(obj); }); it('null/undefined/number 安全返回', () => { expect(truncate(null)).toBe(null); expect(truncate(undefined)).toBe(undefined); expect(truncate(42)).toBe(42); }); }); describe('ToolRegistry.execute', () => { it('未知工具返回错误结果', async () => { const registry = new ToolRegistry(); const result = await registry.execute( { id: 'tc_1', name: 'not_exist', args: {}, iteration: 1, timestamp: Date.now() }, createContext(), ); expect(result.success).toBe(false); expect(result.error).toContain('Unknown tool'); }); it('工具超时返回错误结果', async () => { const registry = new ToolRegistry(); const slowTool: IMetonaTool = { definition: { name: 'slow_tool', description: 'slows', parameters: { type: 'object', properties: {} }, category: 'CODE_EXECUTION' as never, riskLevel: 'SAFE' as never, requiresPermission: false, timeoutMs: 20, }, execute: async () => { await new Promise((r) => setTimeout(r, 200)); return 'too late'; }, }; registry.registerBuiltin(slowTool); const result = await registry.execute( { id: 'tc_2', name: 'slow_tool', args: {}, iteration: 1, timestamp: Date.now() }, createContext(), ); expect(result.success).toBe(false); expect(result.error).toContain('timed out'); }); it('正常执行返回结果', async () => { const registry = new ToolRegistry(); registry.registerBuiltin({ definition: { name: 'fast_tool', description: 'fast', parameters: { type: 'object', properties: {} }, category: 'CODE_EXECUTION' as never, riskLevel: 'SAFE' as never, requiresPermission: false, timeoutMs: 5_000, }, execute: async () => 'done', }); const result = await registry.execute( { id: 'tc_3', name: 'fast_tool', args: {}, iteration: 1, timestamp: Date.now() }, createContext(), ); expect(result.success).toBe(true); expect(result.result).toBe('done'); }); });