99 lines
3.5 KiB
TypeScript
99 lines
3.5 KiB
TypeScript
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 ツール名');
|
|
});
|
|
});
|