Skip to main content
Glama
attribute-validator.test.tsβ€’16.6 kB
/** * Unit tests for attribute validator */ import { validateAttributeValue, ValidationResult, AttributeType, } from '../../../src/validators/attribute-validator.js'; describe('Attribute Validator', () => { describe('validateAttributeValue', () => { // Boolean validation tests describe('Boolean validation', () => { it('should validate native boolean values', () => { expect(validateAttributeValue('is_active', true, 'boolean')).toEqual({ valid: true, convertedValue: true, }); expect(validateAttributeValue('is_active', false, 'boolean')).toEqual({ valid: true, convertedValue: false, }); }); it('should convert string representations to boolean values', () => { // Truthy strings const truthyValues = [ 'true', 'yes', '1', 'on', 'True', 'YES', ' true ', ]; truthyValues.forEach((value) => { expect(validateAttributeValue('is_active', value, 'boolean')).toEqual( { valid: true, convertedValue: true, } ); }); // Falsy strings const falsyValues = [ 'false', 'no', '0', 'off', 'False', 'NO', ' false ', ]; falsyValues.forEach((value) => { expect(validateAttributeValue('is_active', value, 'boolean')).toEqual( { valid: true, convertedValue: false, } ); }); }); it('should convert numeric values to boolean values', () => { expect(validateAttributeValue('is_active', 1, 'boolean')).toEqual({ valid: true, convertedValue: true, }); expect(validateAttributeValue('is_active', 0, 'boolean')).toEqual({ valid: true, convertedValue: false, }); }); it('should reject unconvertible values with appropriate error messages', () => { const invalidValues = ['maybe', 'invalid', 2, {}, []]; invalidValues.forEach((value) => { const result = validateAttributeValue('is_active', value, 'boolean'); expect(result.valid).toBe(false); expect(result.error).toContain('Invalid boolean value'); expect(result.error).toContain('is_active'); }); }); }); // Number validation tests describe('Number validation', () => { it('should validate native number values', () => { expect(validateAttributeValue('revenue', 10000, 'number')).toEqual({ valid: true, convertedValue: 10000, }); expect(validateAttributeValue('revenue', 0, 'number')).toEqual({ valid: true, convertedValue: 0, }); expect(validateAttributeValue('revenue', -50.75, 'number')).toEqual({ valid: true, convertedValue: -50.75, }); }); it('should convert numeric strings to numbers', () => { expect(validateAttributeValue('revenue', '10000', 'number')).toEqual({ valid: true, convertedValue: 10000, }); expect(validateAttributeValue('revenue', '0', 'number')).toEqual({ valid: true, convertedValue: 0, }); expect(validateAttributeValue('revenue', '-50.75', 'number')).toEqual({ valid: true, convertedValue: -50.75, }); expect(validateAttributeValue('revenue', ' 12345 ', 'number')).toEqual({ valid: true, convertedValue: 12345, }); }); it('should convert boolean values to numbers', () => { expect(validateAttributeValue('binary_value', true, 'number')).toEqual({ valid: true, convertedValue: 1, }); expect(validateAttributeValue('binary_value', false, 'number')).toEqual( { valid: true, convertedValue: 0, } ); }); it('should reject unconvertible values with appropriate error messages', () => { const invalidValues = ['not-a-number', 'abc123', {}, [], 'NaN']; invalidValues.forEach((value) => { const result = validateAttributeValue('revenue', value, 'number'); expect(result.valid).toBe(false); expect(result.error).toContain('Invalid number value'); expect(result.error).toContain('revenue'); }); }); }); // String validation tests describe('String validation', () => { it('should validate native string values', () => { expect(validateAttributeValue('name', 'Acme Corp', 'string')).toEqual({ valid: true, convertedValue: 'Acme Corp', }); expect(validateAttributeValue('name', '', 'string')).toEqual({ valid: true, convertedValue: '', }); }); it('should convert number values to strings', () => { expect(validateAttributeValue('name', 12345, 'string')).toEqual({ valid: true, convertedValue: '12345', }); expect(validateAttributeValue('name', 0, 'string')).toEqual({ valid: true, convertedValue: '0', }); expect(validateAttributeValue('name', -50.75, 'string')).toEqual({ valid: true, convertedValue: '-50.75', }); }); it('should convert boolean values to strings', () => { expect(validateAttributeValue('name', true, 'string')).toEqual({ valid: true, convertedValue: 'true', }); expect(validateAttributeValue('name', false, 'string')).toEqual({ valid: true, convertedValue: 'false', }); }); it('should convert date objects to ISO strings', () => { const testDate = new Date('2023-05-15T14:30:00Z'); expect( validateAttributeValue('date_string', testDate, 'string') ).toEqual({ valid: true, convertedValue: testDate.toISOString(), }); }); it('should try to convert objects to JSON strings', () => { const testObj = { id: 123, name: 'Test' }; expect( validateAttributeValue('json_string', testObj, 'string') ).toEqual({ valid: true, convertedValue: JSON.stringify(testObj), }); }); }); // Date validation tests describe('Date validation', () => { it('should validate date objects', () => { const testDate = new Date('2023-05-15T14:30:00Z'); expect(validateAttributeValue('created_at', testDate, 'date')).toEqual({ valid: true, convertedValue: testDate.toISOString(), }); }); it('should reject invalid date objects', () => { const invalidDate = new Date('invalid date'); const result = validateAttributeValue( 'created_at', invalidDate, 'date' ); expect(result.valid).toBe(false); expect(result.error).toContain('Invalid date value'); }); it('should convert ISO date strings to dates', () => { const dateStr = '2023-05-15T14:30:00Z'; const expectedDate = new Date(dateStr).toISOString(); const result = validateAttributeValue('created_at', dateStr, 'date'); expect(result.valid).toBe(true); expect(result.convertedValue).toBe(expectedDate); }); it('should convert various date string formats', () => { const dateFormats = ['2023-05-15', '05/15/2023', 'May 15, 2023']; dateFormats.forEach((dateStr) => { const result = validateAttributeValue('created_at', dateStr, 'date'); expect(result.valid).toBe(true); expect(typeof result.convertedValue).toBe('string'); // The format should be an ISO string expect(result.convertedValue).toMatch( /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/ ); }); }); it('should convert Unix timestamps (milliseconds) to date strings', () => { const timestamp = 1684162200000; // 2023-05-15T14:30:00Z const expectedDate = new Date(timestamp).toISOString(); expect(validateAttributeValue('created_at', timestamp, 'date')).toEqual( { valid: true, convertedValue: expectedDate, } ); }); it('should convert Unix timestamps (seconds) to date strings', () => { const timestamp = 1684162200; // 2023-05-15T14:30:00Z in seconds const expectedDate = new Date(timestamp * 1000).toISOString(); expect(validateAttributeValue('created_at', timestamp, 'date')).toEqual( { valid: true, convertedValue: expectedDate, } ); }); it('should reject unconvertible values with appropriate error messages', () => { const invalidValues = ['not-a-date', 'tomorrow', true, {}, []]; invalidValues.forEach((value) => { const result = validateAttributeValue('created_at', value, 'date'); expect(result.valid).toBe(false); expect(result.error).toContain('Invalid date value'); expect(result.error).toContain('created_at'); }); }); }); // Array validation tests describe('Array validation', () => { it('should validate native array values', () => { expect( validateAttributeValue('tags', ['tag1', 'tag2'], 'array') ).toEqual({ valid: true, convertedValue: ['tag1', 'tag2'], }); expect(validateAttributeValue('tags', [], 'array')).toEqual({ valid: true, convertedValue: [], }); }); it('should convert non-array values to single-item arrays', () => { expect(validateAttributeValue('tags', 'single-tag', 'array')).toEqual({ valid: true, convertedValue: ['single-tag'], }); expect(validateAttributeValue('values', 123, 'array')).toEqual({ valid: true, convertedValue: [123], }); expect(validateAttributeValue('flags', true, 'array')).toEqual({ valid: true, convertedValue: [true], }); }); }); // Object validation tests describe('Object validation', () => { it('should validate plain object values', () => { const testObj = { id: 123, name: 'Test' }; expect(validateAttributeValue('metadata', testObj, 'object')).toEqual({ valid: true, convertedValue: testObj, }); expect(validateAttributeValue('metadata', {}, 'object')).toEqual({ valid: true, convertedValue: {}, }); }); it('should reject non-object values', () => { const invalidValues = ['string', 123, true, [1, 2, 3]]; invalidValues.forEach((value) => { const result = validateAttributeValue('metadata', value, 'object'); expect(result.valid).toBe(false); expect(result.error).toContain('Invalid object value'); expect(result.error).toContain('metadata'); }); }); }); // Select validation tests describe('Select validation', () => { it('should validate string option values', () => { expect(validateAttributeValue('status', 'active', 'select')).toEqual({ valid: true, convertedValue: 'active', }); }); it('should validate array of option values', () => { expect( validateAttributeValue('categories', ['software', 'tech'], 'select') ).toEqual({ valid: true, convertedValue: ['software', 'tech'], }); }); it('should convert non-string values to strings in arrays', () => { expect( validateAttributeValue('categories', [1, true], 'select') ).toEqual({ valid: true, convertedValue: ['1', 'true'], }); }); }); // Record reference validation tests describe('Record reference validation', () => { it('should validate string ID values', () => { expect( validateAttributeValue( 'parent_company', 'rec_123456', 'record-reference' ) ).toEqual({ valid: true, convertedValue: 'rec_123456', }); }); it('should extract record_id from objects', () => { expect( validateAttributeValue( 'parent_company', { record_id: 'rec_123456' }, 'record-reference' ) ).toEqual({ valid: true, convertedValue: 'rec_123456', }); }); it('should extract id from objects as fallback', () => { expect( validateAttributeValue( 'parent_company', { id: 'rec_123456' }, 'record-reference' ) ).toEqual({ valid: true, convertedValue: 'rec_123456', }); }); it('should handle arrays of record references', () => { const input = ['rec_123', { record_id: 'rec_456' }, { id: 'rec_789' }]; expect( validateAttributeValue('related_companies', input, 'record-reference') ).toEqual({ valid: true, convertedValue: ['rec_123', 'rec_456', 'rec_789'], }); }); it('should reject invalid record references', () => { const invalidValues = [{ name: 'Not a record' }, true, 123]; invalidValues.forEach((value) => { const result = validateAttributeValue( 'parent_company', value, 'record-reference' ); expect(result.valid).toBe(false); expect(result.error).toContain('Invalid record reference value'); expect(result.error).toContain('parent_company'); }); }); }); // Null/undefined handling tests describe('Null and undefined value handling', () => { it('should treat null values as valid for any type', () => { const types: AttributeType[] = [ 'string', 'number', 'boolean', 'date', 'array', 'object', 'select', 'record-reference', ]; types.forEach((type) => { expect(validateAttributeValue('any_field', null, type)).toEqual({ valid: true, convertedValue: null, }); }); }); it('should treat undefined values as valid and convert to null', () => { const types: AttributeType[] = [ 'string', 'number', 'boolean', 'date', 'array', 'object', 'select', 'record-reference', ]; types.forEach((type) => { expect(validateAttributeValue('any_field', undefined, type)).toEqual({ valid: true, convertedValue: null, }); }); }); }); // Edge case handling describe('Edge case handling', () => { it('should handle empty strings appropriately by type', () => { // For strings, empty string is valid expect(validateAttributeValue('name', '', 'string')).toEqual({ valid: true, convertedValue: '', }); // For numbers, empty string should be NaN (invalid) const numberResult = validateAttributeValue('count', '', 'number'); expect(numberResult.valid).toBe(false); // For booleans, empty string should be invalid const boolResult = validateAttributeValue('is_active', '', 'boolean'); expect(boolResult.valid).toBe(false); }); it('should handle whitespace strings appropriately', () => { // For strings, whitespace is valid expect(validateAttributeValue('name', ' ', 'string')).toEqual({ valid: true, convertedValue: ' ', }); // For numbers, whitespace should be invalid const numberResult = validateAttributeValue('count', ' ', 'number'); expect(numberResult.valid).toBe(false); // For booleans, whitespace should be invalid const boolResult = validateAttributeValue( 'is_active', ' ', 'boolean' ); expect(boolResult.valid).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/kesslerio/attio-mcp-server'

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