maestro/src/engine/tools/docs.test.ts
2026-06-03 05:08:00 +00:00

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 ツール名');
});
});