vibe-coder-mcp
Version:
Production-ready MCP server with complete agent integration, multi-transport support, and comprehensive development automation tools for AI-assisted workflows.
110 lines (109 loc) • 5.25 kB
JavaScript
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { z } from 'zod';
import { registerTool, executeTool, clearRegistryForTesting } from './toolRegistry.js';
import logger from '../../logger.js';
vi.mock('../../logger.js', () => ({
default: {
info: vi.fn(),
debug: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
}
}));
const mockConfig = { baseUrl: 'test', apiKey: 'test', geminiModel: 'test', perplexityModel: 'test' };
const mockSuccessExecutor = vi.fn();
const mockErrorExecutor = vi.fn();
const mockThrowingExecutor = vi.fn();
const successToolSchemaShape = { message: z.string() };
const errorToolSchemaShape = { id: z.number() };
const throwingToolSchemaShape = {};
const successToolDef = {
name: 'successTool',
description: 'A tool that always succeeds',
inputSchema: successToolSchemaShape,
executor: mockSuccessExecutor,
};
const errorToolDef = {
name: 'errorTool',
description: 'A tool that returns an error result',
inputSchema: errorToolSchemaShape,
executor: mockErrorExecutor,
};
const throwingToolDef = {
name: 'throwingTool',
description: 'A tool executor that throws',
inputSchema: throwingToolSchemaShape,
executor: mockThrowingExecutor,
};
describe('Tool Registry Integration (executeTool)', () => {
beforeEach(() => {
process.env.NODE_ENV = 'test';
clearRegistryForTesting();
mockSuccessExecutor.mockReset().mockResolvedValue({
content: [{ type: 'text', text: 'Success!' }],
isError: false,
});
mockErrorExecutor.mockReset().mockResolvedValue({
content: [{ type: 'text', text: 'Executor failed' }],
isError: true,
errorDetails: { type: 'MockExecutorError', message: 'Executor failed' }
});
mockThrowingExecutor.mockReset().mockRejectedValue(new Error('Unexpected throw'));
vi.mocked(logger.info).mockClear();
vi.mocked(logger.debug).mockClear();
vi.mocked(logger.warn).mockClear();
vi.mocked(logger.error).mockClear();
registerTool(successToolDef);
registerTool(errorToolDef);
registerTool(throwingToolDef);
});
afterEach(() => {
clearRegistryForTesting();
});
it('should execute a registered tool successfully with valid params', async () => {
const params = { message: 'hello' };
const result = await executeTool('successTool', params, mockConfig);
expect(result.isError).toBe(false);
expect(result.content?.[0]?.text).toBe('Success!');
expect(mockSuccessExecutor).toHaveBeenCalledTimes(1);
expect(mockSuccessExecutor).toHaveBeenCalledWith(params, mockConfig, undefined);
expect(vi.mocked(logger.error)).not.toHaveBeenCalled();
});
it('should return error result if executor returns isError: true', async () => {
const params = { id: 123 };
const result = await executeTool('errorTool', params, mockConfig);
expect(result.isError).toBe(true);
expect(result.content?.[0]?.text).toBe('Executor failed');
expect(result.errorDetails).toBeDefined();
expect(result.errorDetails?.type).toBe('MockExecutorError');
expect(mockErrorExecutor).toHaveBeenCalledTimes(1);
expect(mockErrorExecutor).toHaveBeenCalledWith(params, mockConfig, undefined);
expect(vi.mocked(logger.error)).not.toHaveBeenCalled();
});
it('should return error result if executor throws an error', async () => {
const params = {};
const result = await executeTool('throwingTool', params, mockConfig);
expect(result.isError).toBe(true);
expect(result.content?.[0]?.text).toContain("Unexpected error in tool 'throwingTool': Unexpected throw");
expect(result.errorDetails).toBeDefined();
expect(result.errorDetails?.type).toBe('Error');
expect(mockThrowingExecutor).toHaveBeenCalledTimes(1);
expect(vi.mocked(logger.error)).toHaveBeenCalledWith(expect.objectContaining({ tool: 'throwingTool' }), expect.stringContaining("Error during execution"));
});
it('should return validation error result for invalid params', async () => {
const invalidParams = { message: 123 };
const result = await executeTool('successTool', invalidParams, mockConfig);
expect(result.isError).toBe(true);
expect(result.content?.[0]?.text).toContain("Input validation failed for tool 'successTool'");
expect(result.errorDetails).toBeDefined();
expect(result.errorDetails?.type).toBe('ValidationError');
expect(mockSuccessExecutor).not.toHaveBeenCalled();
expect(vi.mocked(logger.error)).toHaveBeenCalledWith(expect.objectContaining({ tool: 'successTool' }), 'Tool parameter validation failed.');
});
it('should return error result for unregistered tool', async () => {
const result = await executeTool('nonExistentTool', {}, mockConfig);
expect(result.isError).toBe(true);
expect(result.content?.[0]?.text).toBe('Error: Tool "nonExistentTool" not found.');
expect(vi.mocked(logger.error)).toHaveBeenCalledWith('Tool "nonExistentTool" not found in registry.');
});
});