Skip to main content
Glama
registry.test.ts11.3 kB
/** * @file Unified Tools Tests * @description Unit tests for unified tool system */ import { describe, it, expect, beforeEach } from 'vitest'; import { z } from 'zod'; import { UnifiedToolDefinition, UnifiedToolRegistry, ToolContext, ToolResult, } from '../base.js'; describe('Unified Tool System', () => { let registry: UnifiedToolRegistry; beforeEach(() => { registry = new UnifiedToolRegistry(); }); describe('Tool Registration', () => { it('should register a tool successfully', () => { const tool: UnifiedToolDefinition = { name: 'test.tool', description: 'Test tool', category: 'system', inputSchema: z.object({ input: z.string() }), handler: async (input) => ({ success: true, data: { output: input.input }, }), }; registry.register(tool); const retrieved = registry.get('test.tool'); expect(retrieved).toBeDefined(); expect(retrieved?.name).toBe('test.tool'); }); it('should list tools by category', () => { const aiTool: UnifiedToolDefinition = { name: 'ai.test', description: 'AI test', category: 'ai', inputSchema: z.object({}), handler: async () => ({ success: true, data: {} }), }; const dbTool: UnifiedToolDefinition = { name: 'db.test', description: 'DB test', category: 'database', inputSchema: z.object({}), handler: async () => ({ success: true, data: {} }), }; registry.register(aiTool); registry.register(dbTool); const aiTools = registry.list({ category: 'ai' }); expect(aiTools).toHaveLength(1); expect(aiTools[0].name).toBe('ai.test'); const dbTools = registry.list({ category: 'database' }); expect(dbTools).toHaveLength(1); expect(dbTools[0].name).toBe('db.test'); }); it('should unregister a tool', () => { const tool: UnifiedToolDefinition = { name: 'test.remove', description: 'Test', category: 'system', inputSchema: z.object({}), handler: async () => ({ success: true, data: {} }), }; registry.register(tool); expect(registry.get('test.remove')).toBeDefined(); const removed = registry.unregister('test.remove'); expect(removed).toBe(true); expect(registry.get('test.remove')).toBeUndefined(); }); }); describe('Tool Execution', () => { it('should execute a tool successfully', async () => { const tool: UnifiedToolDefinition<{ name: string }, { greeting: string }> = { name: 'test.greet', description: 'Greeting tool', category: 'system', inputSchema: z.object({ name: z.string(), }), handler: async (input) => ({ success: true, data: { greeting: `Hello, ${input.name}!` }, }), }; registry.register(tool); const context: ToolContext = { executionId: 'test-1', }; const result = await registry.execute('test.greet', { name: 'World' }, context); expect(result.success).toBe(true); expect(result.data).toEqual({ greeting: 'Hello, World!' }); expect(result.metadata?.duration).toBeGreaterThan(0); }); it('should validate input with Zod schema', async () => { const tool: UnifiedToolDefinition = { name: 'test.validate', description: 'Validation test', category: 'system', inputSchema: z.object({ email: z.string().email(), age: z.number().min(0).max(150), }), handler: async (input) => ({ success: true, data: input, }), }; registry.register(tool); const context: ToolContext = { executionId: 'test-2' }; // Valid input const validResult = await registry.execute( 'test.validate', { email: 'test@example.com', age: 25 }, context ); expect(validResult.success).toBe(true); // Invalid input const invalidResult = await registry.execute( 'test.validate', { email: 'invalid-email', age: 25 }, context ); expect(invalidResult.success).toBe(false); expect(invalidResult.error?.code).toBeTruthy(); }); it('should handle tool errors gracefully', async () => { const tool: UnifiedToolDefinition = { name: 'test.error', description: 'Error test', category: 'system', inputSchema: z.object({}), handler: async () => { throw new Error('Test error'); }, }; registry.register(tool); const context: ToolContext = { executionId: 'test-3' }; const result = await registry.execute('test.error', {}, context); expect(result.success).toBe(false); expect(result.error?.message).toBe('Test error'); }); it('should track tool statistics', async () => { const tool: UnifiedToolDefinition = { name: 'test.stats', description: 'Stats test', category: 'system', inputSchema: z.object({}), handler: async () => ({ success: true, data: {}, }), }; registry.register(tool); const context: ToolContext = { executionId: 'test-4' }; // Execute 3 times await registry.execute('test.stats', {}, context); await registry.execute('test.stats', {}, context); await registry.execute('test.stats', {}, context); const stats = registry.getStats('test.stats'); expect(stats).toBeDefined(); expect(stats?.callCount).toBe(3); expect(stats?.errorCount).toBe(0); expect(stats?.avgDuration).toBeGreaterThan(0); }); }); describe('Middleware', () => { it('should run before middleware', async () => { const calls: string[] = []; registry.use({ before: async (tool, input, context) => { calls.push(`before:${tool.name}`); }, }); const tool: UnifiedToolDefinition = { name: 'test.middleware', description: 'Middleware test', category: 'system', inputSchema: z.object({}), handler: async () => { calls.push('handler'); return { success: true, data: {} }; }, }; registry.register(tool); const context: ToolContext = { executionId: 'test-5' }; await registry.execute('test.middleware', {}, context); expect(calls).toEqual(['before:test.middleware', 'handler']); }); it('should run after middleware', async () => { const calls: string[] = []; registry.use({ after: async (tool, input, result, context) => { calls.push(`after:${tool.name}:${result.success}`); }, }); const tool: UnifiedToolDefinition = { name: 'test.after', description: 'After test', category: 'system', inputSchema: z.object({}), handler: async () => { calls.push('handler'); return { success: true, data: {} }; }, }; registry.register(tool); const context: ToolContext = { executionId: 'test-6' }; await registry.execute('test.after', {}, context); expect(calls).toEqual(['handler', 'after:test.after:true']); }); it('should run error middleware on failures', async () => { let errorCaught = false; registry.use({ onError: async (tool, input, error, context) => { errorCaught = true; }, }); const tool: UnifiedToolDefinition = { name: 'test.error.mw', description: 'Error middleware test', category: 'system', inputSchema: z.object({}), handler: async () => { throw new Error('Test error'); }, }; registry.register(tool); const context: ToolContext = { executionId: 'test-7' }; await registry.execute('test.error.mw', {}, context); expect(errorCaught).toBe(true); }); }); describe('Tool Filtering', () => { beforeEach(() => { registry.register({ name: 'ai.chat', description: 'Chat', category: 'ai', inputSchema: z.object({}), handler: async () => ({ success: true, data: {} }), metadata: { tags: ['chat', 'llm'] }, }); registry.register({ name: 'ai.code', description: 'Code', category: 'ai', inputSchema: z.object({}), handler: async () => ({ success: true, data: {} }), metadata: { tags: ['code', 'generation'] }, }); registry.register({ name: 'db.query', description: 'Query', category: 'database', inputSchema: z.object({}), handler: async () => ({ success: true, data: {} }), metadata: { tags: ['query', 'sql'] }, }); }); it('should filter by tags', () => { const chatTools = registry.list({ tags: ['chat'] }); expect(chatTools).toHaveLength(1); expect(chatTools[0].name).toBe('ai.chat'); const codeTools = registry.list({ tags: ['code'] }); expect(codeTools).toHaveLength(1); expect(codeTools[0].name).toBe('ai.code'); }); it('should combine category and tag filters', () => { const aiChatTools = registry.list({ category: 'ai', tags: ['chat'], }); expect(aiChatTools).toHaveLength(1); expect(aiChatTools[0].name).toBe('ai.chat'); }); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/babasida246/ai-mcp-gateway'

If you have feedback or need assistance with the MCP directory API, please join our Discord server