Skip to main content
Glama
field-suggestions.test.tsβ€’8.08 kB
/** * Tests for field suggestion utilities */ import { describe, it, expect } from 'vitest'; import { levenshteinDistance, findSimilarOptions, generateFieldSuggestionMessage, generateEnumSuggestionMessage, generateReadOnlyFieldMessage, generateResourceTypeSuggestionMessage, getMappedFieldName, validateFieldWithSuggestions, VALID_RESOURCE_TYPES, } from '../../src/utils/field-suggestions.js'; describe('Field Suggestions Utilities', () => { describe('levenshteinDistance', () => { it('should calculate correct distance for similar strings', () => { expect(levenshteinDistance('hello', 'hallo')).toBe(1); expect(levenshteinDistance('name', 'names')).toBe(1); expect(levenshteinDistance('first_name', 'firstname')).toBe(1); }); it('should return 0 for identical strings', () => { expect(levenshteinDistance('test', 'test')).toBe(0); }); it('should handle case differences', () => { expect(levenshteinDistance('Name', 'name')).toBe(1); }); }); describe('findSimilarOptions', () => { const validFields = [ 'first_name', 'last_name', 'email', 'phone_numbers', 'job_title', ]; it('should find similar field names', () => { const suggestions = findSimilarOptions('firstname', validFields); expect(suggestions).toContain('first_name'); }); it('should find multiple suggestions sorted by similarity', () => { const suggestions = findSimilarOptions('name', validFields); expect(suggestions).toContain('first_name'); expect(suggestions).toContain('last_name'); }); it('should respect max suggestions limit', () => { const suggestions = findSimilarOptions('name', validFields, 1); expect(suggestions).toHaveLength(1); }); it('should handle substring matching when no close matches', () => { const suggestions = findSimilarOptions('phone', validFields); expect(suggestions).toContain('phone_numbers'); }); it('should return empty array for no matches', () => { const suggestions = findSimilarOptions('xyz123', validFields); expect(suggestions).toEqual([]); }); }); describe('generateFieldSuggestionMessage', () => { const validFields = ['first_name', 'last_name', 'email']; it('should generate message with suggestions for similar field', () => { const message = generateFieldSuggestionMessage('firstname', validFields); expect(message).toContain('Invalid field name: "firstname"'); expect(message).toContain('Did you mean: "first_name"'); }); it('should include context when provided', () => { const message = generateFieldSuggestionMessage( 'firstname', validFields, 'people' ); expect(message).toContain('for people'); }); it('should show valid fields when no close matches', () => { const message = generateFieldSuggestionMessage('xyz', validFields); expect(message).toContain('Valid fields include:'); expect(message).toContain('"first_name"'); }); it('should handle large field lists', () => { const manyFields = Array.from({ length: 20 }, (_, i) => `field_${i}`); const message = generateFieldSuggestionMessage('xyz', manyFields); expect(message).toContain('(and 15 more)'); }); }); describe('generateEnumSuggestionMessage', () => { const validValues = ['active', 'inactive', 'pending']; it('should generate message with all valid options for small sets', () => { const message = generateEnumSuggestionMessage( 'activ', validValues, 'status' ); expect(message).toContain('Invalid value "activ" for field "status"'); expect(message).toContain('Did you mean: "active"'); expect(message).toContain('Valid options are:'); expect(message).toContain('"active", "inactive", "pending"'); }); it('should truncate large option lists', () => { const manyValues = Array.from({ length: 20 }, (_, i) => `option_${i}`); const message = generateEnumSuggestionMessage('opt', manyValues, 'field'); expect(message).toContain('Valid options include:'); expect(message).toContain('(and 15 more)'); }); }); describe('generateReadOnlyFieldMessage', () => { it('should generate message for update operation', () => { const message = generateReadOnlyFieldMessage('created_at', 'update'); expect(message).toContain('Field "created_at" is read-only'); expect(message).toContain('cannot be modified'); }); it('should generate message for create operation', () => { const message = generateReadOnlyFieldMessage('id', 'create'); expect(message).toContain('Field "id" is read-only'); expect(message).toContain('cannot be set during creation'); }); }); describe('generateResourceTypeSuggestionMessage', () => { it('should suggest similar resource types', () => { const message = generateResourceTypeSuggestionMessage( 'person', VALID_RESOURCE_TYPES ); expect(message).toContain('Invalid resource type: "person"'); expect(message).toContain('Did you mean: "people"'); }); it('should list all valid resource types', () => { const message = generateResourceTypeSuggestionMessage( 'xyz', VALID_RESOURCE_TYPES ); expect(message).toContain('Valid resource types are:'); expect(message).toContain('"people"'); expect(message).toContain('"companies"'); }); }); describe('getMappedFieldName', () => { it('should map common field name mistakes', () => { expect(getMappedFieldName('firstname')).toBe('first_name'); expect(getMappedFieldName('lastname')).toBe('last_name'); expect(getMappedFieldName('fullname')).toBe('name'); expect(getMappedFieldName('phone')).toBe('phone_numbers'); expect(getMappedFieldName('email')).toBe('email_addresses'); }); it('should handle different formats', () => { expect(getMappedFieldName('first-name')).toBe('first_name'); expect(getMappedFieldName('first name')).toBe('first_name'); expect(getMappedFieldName('FIRSTNAME')).toBe('first_name'); }); it('should return null for unknown fields', () => { expect(getMappedFieldName('unknown_field')).toBeNull(); }); }); describe('validateFieldWithSuggestions', () => { const validFields = [ 'first_name', 'last_name', 'email', 'created_at', 'updated_at', ]; const readOnlyFields = ['created_at', 'updated_at']; it('should validate valid fields', () => { const result = validateFieldWithSuggestions('first_name', validFields); expect(result.valid).toBe(true); expect(result.error).toBeUndefined(); }); it('should suggest mapped field names', () => { const result = validateFieldWithSuggestions('firstname', validFields); expect(result.valid).toBe(false); expect(result.error).toContain('Did you mean "first_name"'); }); it('should generate suggestions for unknown fields', () => { const result = validateFieldWithSuggestions('name', validFields); expect(result.valid).toBe(false); expect(result.error).toContain('Did you mean:'); }); it('should detect read-only fields for update', () => { const result = validateFieldWithSuggestions( 'created_at', validFields, readOnlyFields, 'update' ); expect(result.valid).toBe(false); expect(result.error).toContain('read-only'); expect(result.error).toContain('cannot be modified'); }); it('should detect read-only fields for create', () => { const result = validateFieldWithSuggestions( 'created_at', validFields, readOnlyFields, 'create' ); expect(result.valid).toBe(false); expect(result.error).toContain('read-only'); expect(result.error).toContain('cannot be set during creation'); }); }); });

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/kesslerio/attio-mcp-server'

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