import { describe, it, expect, afterEach } from 'vitest'; import { TOOL_DEFS, executeTool, setMcpToolLookup } from './docs.js'; describe('docs tool (ReadToolDoc)', () => { it('exports ReadToolDoc definition', () => { expect(TOOL_DEFS).toHaveProperty('ReadToolDoc'); }); it('reads existing tool doc', async () => { const result = await executeTool('ReadToolDoc', { name: 'BrowseWeb' }, { workspacePath: '/tmp/test', editAllowed: false, }); expect(result).not.toBeNull(); expect(result!.isError).toBe(false); expect(result!.output).toContain('BrowseWeb'); }); it('returns error and lists available docs for unknown tool', async () => { const result = await executeTool('ReadToolDoc', { name: 'NonExistent' }, { workspacePath: '/tmp/test', editAllowed: false, }); expect(result!.isError).toBe(true); expect(result!.output).toContain('利用可能なドキュメント'); expect(result!.output).toContain('browseweb'); }); it('rejects path traversal attempts', async () => { const result = await executeTool('ReadToolDoc', { name: '../../etc/passwd' }, { workspacePath: '/tmp/test', editAllowed: false, }); expect(result!.isError).toBe(true); expect(result!.output).toContain('不正なツール名'); }); it('rejects empty name', async () => { const result = await executeTool('ReadToolDoc', {}, { workspacePath: '/tmp/test', editAllowed: false, }); expect(result!.isError).toBe(true); expect(result!.output).toContain('name パラメータが必要'); }); it('returns null for unknown tool name', async () => { const result = await executeTool('SomeOtherTool', {}, { workspacePath: '/tmp/test', editAllowed: false, }); expect(result).toBeNull(); }); }); describe('ReadToolDoc MCP fallback', () => { afterEach(() => { setMcpToolLookup(null); }); it('returns description + schema when lookup hits', async () => { setMcpToolLookup((serverId, toolName) => { if (serverId === 'canva' && toolName === 'create_design') { return { description: 'Create a Canva design', input_schema: JSON.stringify({ type: 'object', properties: { title: { type: 'string' } } }), }; } return null; }); const result = await executeTool('ReadToolDoc', { name: 'mcp__canva__create_design' }, {} as never); expect(result?.isError).toBe(false); expect(result?.output).toContain('Create a Canva design'); expect(result?.output).toContain('Input schema'); expect(result?.output).toContain('"title"'); }); it('returns benign error when MCP subsystem is not initialised', async () => { setMcpToolLookup(null); const result = await executeTool('ReadToolDoc', { name: 'mcp__canva__x' }, {} as never); expect(result?.isError).toBe(true); expect(result?.output).toContain('初期化されていません'); }); it('returns not-found when lookup returns null', async () => { setMcpToolLookup(() => null); const result = await executeTool('ReadToolDoc', { name: 'mcp__canva__unknown_tool' }, {} as never); expect(result?.isError).toBe(false); expect(result?.output).toContain('キャッシュ情報がありません'); }); it('rejects invalid MCP tool name', async () => { setMcpToolLookup(() => null); const result = await executeTool('ReadToolDoc', { name: 'mcp__bad' }, {} as never); expect(result?.isError).toBe(true); expect(result?.output).toContain('不正な MCP ツール名'); }); });