Skip to main content
Glama
portel-dev

NCP - Natural Context Provider

by portel-dev
tool-schema-parser.test.ts•13.2 kB
/** * Comprehensive Tests for ToolSchemaParser * Following ncp-oss3 patterns for 95%+ coverage */ import { describe, it, expect } from '@jest/globals'; import { ToolSchemaParser, ParameterInfo } from '../src/services/tool-schema-parser'; describe('ToolSchemaParser - Comprehensive Coverage', () => { const sampleSchema = { properties: { path: { type: 'string', description: 'File path to read' }, encoding: { type: 'string', description: 'File encoding (optional)' }, maxSize: { type: 'number', description: 'Maximum file size in bytes' }, recursive: { type: 'boolean', description: 'Whether to read recursively' } }, required: ['path', 'maxSize'] }; const emptySchema = {}; const noPropertiesSchema = { required: ['something'] }; const noRequiredSchema = { properties: { optional1: { type: 'string' }, optional2: { type: 'number' } } }; describe('🎯 Parameter Parsing - Core Functionality', () => { it('should parse complete schema with all parameter types', () => { const params = ToolSchemaParser.parseParameters(sampleSchema); expect(params).toHaveLength(4); // Check path parameter (required string) const pathParam = params.find(p => p.name === 'path'); expect(pathParam).toEqual({ name: 'path', type: 'string', required: true, description: 'File path to read' }); // Check encoding parameter (optional string) const encodingParam = params.find(p => p.name === 'encoding'); expect(encodingParam).toEqual({ name: 'encoding', type: 'string', required: false, description: 'File encoding (optional)' }); // Check maxSize parameter (required number) const maxSizeParam = params.find(p => p.name === 'maxSize'); expect(maxSizeParam).toEqual({ name: 'maxSize', type: 'number', required: true, description: 'Maximum file size in bytes' }); // Check recursive parameter (optional boolean) const recursiveParam = params.find(p => p.name === 'recursive'); expect(recursiveParam).toEqual({ name: 'recursive', type: 'boolean', required: false, description: 'Whether to read recursively' }); }); it('should handle schema with missing properties', () => { const params = ToolSchemaParser.parseParameters(noPropertiesSchema); expect(params).toEqual([]); }); it('should handle schema with no required array', () => { const params = ToolSchemaParser.parseParameters(noRequiredSchema); expect(params).toHaveLength(2); params.forEach(param => { expect(param.required).toBe(false); }); }); it('should handle properties without type information', () => { const schemaWithoutTypes = { properties: { mystery1: { description: 'Unknown type parameter' }, mystery2: { /* no type or description */ } }, required: ['mystery1'] }; const params = ToolSchemaParser.parseParameters(schemaWithoutTypes); expect(params).toHaveLength(2); const mystery1 = params.find(p => p.name === 'mystery1'); expect(mystery1).toEqual({ name: 'mystery1', type: 'unknown', required: true, description: 'Unknown type parameter' }); const mystery2 = params.find(p => p.name === 'mystery2'); expect(mystery2).toEqual({ name: 'mystery2', type: 'unknown', required: false, description: undefined }); }); }); describe('🎯 Edge Cases and Error Handling', () => { it('should handle null and undefined schemas', () => { expect(ToolSchemaParser.parseParameters(null)).toEqual([]); expect(ToolSchemaParser.parseParameters(undefined)).toEqual([]); }); it('should handle non-object schemas', () => { expect(ToolSchemaParser.parseParameters('string')).toEqual([]); expect(ToolSchemaParser.parseParameters(123)).toEqual([]); expect(ToolSchemaParser.parseParameters([])).toEqual([]); expect(ToolSchemaParser.parseParameters(true)).toEqual([]); }); it('should handle empty schema object', () => { expect(ToolSchemaParser.parseParameters(emptySchema)).toEqual([]); }); it('should handle schema with null/undefined properties', () => { const badSchema = { properties: null, required: undefined }; expect(ToolSchemaParser.parseParameters(badSchema)).toEqual([]); }); it('should handle schema with non-array required field', () => { const invalidRequiredSchema = { properties: { param1: { type: 'string' } }, required: 'not-an-array' }; const params = ToolSchemaParser.parseParameters(invalidRequiredSchema); expect(params).toHaveLength(1); expect(params[0].required).toBe(false); }); }); describe('🎯 Required Parameters Filtering', () => { it('should extract only required parameters', () => { const requiredParams = ToolSchemaParser.getRequiredParameters(sampleSchema); expect(requiredParams).toHaveLength(2); expect(requiredParams.map(p => p.name)).toEqual(['path', 'maxSize']); requiredParams.forEach(param => { expect(param.required).toBe(true); }); }); it('should return empty array for schema with no required parameters', () => { const requiredParams = ToolSchemaParser.getRequiredParameters(noRequiredSchema); expect(requiredParams).toEqual([]); }); it('should handle invalid schemas in getRequiredParameters', () => { expect(ToolSchemaParser.getRequiredParameters(null)).toEqual([]); expect(ToolSchemaParser.getRequiredParameters({})).toEqual([]); }); }); describe('🎯 Optional Parameters Filtering', () => { it('should extract only optional parameters', () => { const optionalParams = ToolSchemaParser.getOptionalParameters(sampleSchema); expect(optionalParams).toHaveLength(2); expect(optionalParams.map(p => p.name)).toEqual(['encoding', 'recursive']); optionalParams.forEach(param => { expect(param.required).toBe(false); }); }); it('should return all parameters when none are required', () => { const optionalParams = ToolSchemaParser.getOptionalParameters(noRequiredSchema); expect(optionalParams).toHaveLength(2); optionalParams.forEach(param => { expect(param.required).toBe(false); }); }); it('should handle invalid schemas in getOptionalParameters', () => { expect(ToolSchemaParser.getOptionalParameters(null)).toEqual([]); expect(ToolSchemaParser.getOptionalParameters({})).toEqual([]); }); }); describe('🎯 Required Parameters Detection', () => { it('should detect schemas with required parameters', () => { expect(ToolSchemaParser.hasRequiredParameters(sampleSchema)).toBe(true); }); it('should detect schemas without required parameters', () => { expect(ToolSchemaParser.hasRequiredParameters(noRequiredSchema)).toBe(false); expect(ToolSchemaParser.hasRequiredParameters(emptySchema)).toBe(false); }); it('should handle edge cases in hasRequiredParameters', () => { expect(ToolSchemaParser.hasRequiredParameters(null)).toBe(false); expect(ToolSchemaParser.hasRequiredParameters(undefined)).toBe(false); expect(ToolSchemaParser.hasRequiredParameters('string')).toBe(false); expect(ToolSchemaParser.hasRequiredParameters(123)).toBe(false); }); it('should handle schema with empty required array', () => { const emptyRequiredSchema = { properties: { param1: { type: 'string' } }, required: [] }; expect(ToolSchemaParser.hasRequiredParameters(emptyRequiredSchema)).toBe(false); }); it('should handle schema with non-array required field', () => { const invalidRequiredSchema = { properties: { param1: { type: 'string' } }, required: 'not-an-array' }; expect(ToolSchemaParser.hasRequiredParameters(invalidRequiredSchema)).toBe(false); }); }); describe('🎯 Parameter Counting', () => { it('should count all parameter types correctly', () => { const counts = ToolSchemaParser.countParameters(sampleSchema); expect(counts).toEqual({ total: 4, required: 2, optional: 2 }); }); it('should count parameters in schema with no required fields', () => { const counts = ToolSchemaParser.countParameters(noRequiredSchema); expect(counts).toEqual({ total: 2, required: 0, optional: 2 }); }); it('should count parameters in schema with all required fields', () => { const allRequiredSchema = { properties: { param1: { type: 'string' }, param2: { type: 'number' } }, required: ['param1', 'param2'] }; const counts = ToolSchemaParser.countParameters(allRequiredSchema); expect(counts).toEqual({ total: 2, required: 2, optional: 0 }); }); it('should handle empty schemas in countParameters', () => { expect(ToolSchemaParser.countParameters(emptySchema)).toEqual({ total: 0, required: 0, optional: 0 }); expect(ToolSchemaParser.countParameters(null)).toEqual({ total: 0, required: 0, optional: 0 }); }); }); describe('🎯 Individual Parameter Lookup', () => { it('should find existing parameters by name', () => { const pathParam = ToolSchemaParser.getParameter(sampleSchema, 'path'); expect(pathParam).toEqual({ name: 'path', type: 'string', required: true, description: 'File path to read' }); const encodingParam = ToolSchemaParser.getParameter(sampleSchema, 'encoding'); expect(encodingParam).toEqual({ name: 'encoding', type: 'string', required: false, description: 'File encoding (optional)' }); }); it('should return undefined for non-existent parameters', () => { expect(ToolSchemaParser.getParameter(sampleSchema, 'nonexistent')).toBeUndefined(); expect(ToolSchemaParser.getParameter(sampleSchema, '')).toBeUndefined(); }); it('should handle invalid schemas in getParameter', () => { expect(ToolSchemaParser.getParameter(null, 'any')).toBeUndefined(); expect(ToolSchemaParser.getParameter({}, 'any')).toBeUndefined(); expect(ToolSchemaParser.getParameter('invalid', 'any')).toBeUndefined(); }); it('should handle case-sensitive parameter names', () => { expect(ToolSchemaParser.getParameter(sampleSchema, 'Path')).toBeUndefined(); expect(ToolSchemaParser.getParameter(sampleSchema, 'PATH')).toBeUndefined(); expect(ToolSchemaParser.getParameter(sampleSchema, 'path')).toBeDefined(); }); }); describe('🎯 Complex Schema Scenarios', () => { it('should handle nested object schemas', () => { const nestedSchema = { properties: { config: { type: 'object', description: 'Configuration object', properties: { nested: { type: 'string' } } } }, required: ['config'] }; const params = ToolSchemaParser.parseParameters(nestedSchema); expect(params).toHaveLength(1); expect(params[0]).toEqual({ name: 'config', type: 'object', required: true, description: 'Configuration object' }); }); it('should handle array type schemas', () => { const arraySchema = { properties: { items: { type: 'array', description: 'List of items', items: { type: 'string' } } }, required: ['items'] }; const params = ToolSchemaParser.parseParameters(arraySchema); expect(params[0]).toEqual({ name: 'items', type: 'array', required: true, description: 'List of items' }); }); it('should handle schemas with special characters in property names', () => { const specialSchema = { properties: { 'kebab-case': { type: 'string' }, 'snake_case': { type: 'number' }, 'dot.notation': { type: 'boolean' }, 'space name': { type: 'string' } }, required: ['kebab-case', 'space name'] }; const params = ToolSchemaParser.parseParameters(specialSchema); expect(params).toHaveLength(4); const kebabParam = params.find(p => p.name === 'kebab-case'); expect(kebabParam?.required).toBe(true); const spaceParam = params.find(p => p.name === 'space name'); expect(spaceParam?.required).toBe(true); const snakeParam = params.find(p => p.name === 'snake_case'); expect(snakeParam?.required).toBe(false); }); }); });

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/portel-dev/ncp'

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