UNPKG

@the_cfdude/productboard-mcp

Version:

Model Context Protocol server for Productboard REST API with dynamic tool loading

140 lines (122 loc) 4.62 kB
/** * Tests for Features, Components, and Products tools */ import { describe, it, expect, jest, beforeEach } from '@jest/globals'; import { handleFeaturesTool, setupFeaturesTools, } from '../../tools/features.js'; import type { ToolDefinition } from '../../types/tool-types.js'; describe('Features Tools', () => { beforeEach(() => { jest.clearAllMocks(); }); describe('Tool Definitions', () => { it('should define all features tools with correct schemas', () => { const tools = setupFeaturesTools(); expect(tools.length).toBe(6); const toolNames = tools.map((tool: ToolDefinition) => tool.name); // Features tools expect(toolNames).toContain('create_feature'); expect(toolNames).toContain('get_features'); expect(toolNames).toContain('get_feature'); expect(toolNames).toContain('update_feature'); expect(toolNames).toContain('delete_feature'); expect(toolNames).toContain('get_available_fields'); }); it('should have standardized parameters for list operations', () => { const tools = setupFeaturesTools(); const getFeatures = tools.find( (t: ToolDefinition) => t.name === 'get_features' ); expect(getFeatures?.inputSchema.properties).toHaveProperty('limit'); expect(getFeatures?.inputSchema.properties).toHaveProperty('startWith'); expect(getFeatures?.inputSchema.properties).toHaveProperty('detail'); expect(getFeatures?.inputSchema.properties).toHaveProperty( 'includeSubData' ); }); it('should have standardized parameters for get operations', () => { const tools = setupFeaturesTools(); const getFeature = tools.find( (t: ToolDefinition) => t.name === 'get_feature' ); expect(getFeature?.inputSchema.properties).toHaveProperty('detail'); expect(getFeature?.inputSchema.properties).toHaveProperty( 'includeSubData' ); }); }); describe('Tool Handler', () => { it('should handle unknown tool error', async () => { await expect(handleFeaturesTool('unknown_tool', {})).rejects.toThrow( 'Unknown tool: unknown_tool' ); }); it('should accept valid tool names', () => { const validTools = [ 'create_feature', 'get_features', 'get_feature', 'update_feature', 'delete_feature', 'get_available_fields', ]; validTools.forEach(toolName => { expect(() => { // Just check it doesn't throw immediately const promise = handleFeaturesTool(toolName, {}); // Catch the expected error about missing required fields promise.catch(() => {}); }).not.toThrow('Unknown features tool'); }); }); it('should include timeframe in update_feature schema', () => { const tools = setupFeaturesTools(); const updateFeature = tools.find( (t: ToolDefinition) => t.name === 'update_feature' ); expect(updateFeature?.inputSchema.properties).toHaveProperty('timeframe'); expect((updateFeature?.inputSchema.properties as any).timeframe).toEqual({ type: 'object', description: 'Feature timeframe with start and end dates', properties: { startDate: { type: 'string', description: 'Start date (YYYY-MM-DD)', }, endDate: { type: 'string', description: 'End date (YYYY-MM-DD)' }, granularity: { type: 'string', description: 'Timeframe granularity (optional)', }, }, }); }); it('should handle update_feature with timeframe parameter', async () => { // Test that the function accepts timeframe without throwing expect(() => { const promise = handleFeaturesTool('update_feature', { id: 'test-feature-id', timeframe: { startDate: '2025-02-01', endDate: '2025-02-28', }, }); // Catch expected error about missing API context promise.catch(() => {}); }).not.toThrow(); }); it('should handle update_feature with timeframe as JSON string', async () => { // Test that the function accepts timeframe JSON string without throwing expect(() => { const promise = handleFeaturesTool('update_feature', { id: 'test-feature-id', timeframe: '{"startDate":"2025-02-01","endDate":"2025-02-28"}', }); // Catch expected error about missing API context promise.catch(() => {}); }).not.toThrow(); }); }); });