Skip to main content
Glama
validation.test.tsβ€’8.08 kB
import { describe, it, expect, vi } from 'vitest'; import { validateInput, validateRequest, ValidationSchema, } from '../../src/utils/validation'; import { ErrorType } from '../../src/utils/error-handler'; describe('validation', () => { describe('validateInput', () => { it('should validate a simple object against a schema', () => { const input = { name: 'Test User', age: 30, email: 'test@example.com', }; const schema: ValidationSchema = { type: 'object', required: ['name', 'email'], properties: { name: { type: 'string' }, age: { type: 'number' }, email: { type: 'string' }, }, }; const result = validateInput(input, schema); expect(result.isValid).toBe(true); expect(result.errors).toHaveLength(0); }); it('should detect missing required properties', () => { const input = { name: 'Test User', }; const schema: ValidationSchema = { type: 'object', required: ['name', 'email'], properties: { name: { type: 'string' }, email: { type: 'string' }, }, }; const result = validateInput(input, schema); expect(result.isValid).toBe(false); expect(result.errors).toHaveLength(1); expect(result.errors[0]).toContain('email'); expect(result.errors[0]).toContain('missing'); }); it('should validate type mismatches', () => { const input = { name: 'Test User', age: 'thirty', }; const schema: ValidationSchema = { type: 'object', properties: { name: { type: 'string' }, age: { type: 'number' }, }, }; const result = validateInput(input, schema); expect(result.isValid).toBe(false); expect(result.errors).toHaveLength(1); expect(result.errors[0]).toContain('age'); expect(result.errors[0]).toContain('number'); }); it('should validate nested objects', () => { const input = { name: 'Test User', address: { street: '123 Main St', city: 'Testville', zip: 12345, }, }; const schema: ValidationSchema = { type: 'object', required: ['name', 'address'], properties: { name: { type: 'string' }, address: { type: 'object', required: ['street', 'city', 'zip'], properties: { street: { type: 'string' }, city: { type: 'string' }, zip: { type: 'number' }, }, }, }, }; const result = validateInput(input, schema); expect(result.isValid).toBe(true); expect(result.errors).toHaveLength(0); }); it('should validate arrays', () => { const input = { name: 'Test User', tags: ['tag1', 'tag2', 'tag3'], }; const schema: ValidationSchema = { type: 'object', properties: { name: { type: 'string' }, tags: { type: 'array', items: { type: 'string' }, }, }, }; const result = validateInput(input, schema); expect(result.isValid).toBe(true); expect(result.errors).toHaveLength(0); }); it('should validate array items', () => { const input = { name: 'Test User', tags: ['tag1', 'tag2', 123], }; const schema: ValidationSchema = { type: 'object', properties: { name: { type: 'string' }, tags: { type: 'array', items: { type: 'string' }, }, }, }; const result = validateInput(input, schema); expect(result.isValid).toBe(false); expect(result.errors).toHaveLength(1); expect(result.errors[0]).toContain('tags[2]'); expect(result.errors[0]).toContain('string'); }); it('should validate string constraints', () => { const input = { username: 'user', password: 'pass', }; const schema: ValidationSchema = { type: 'object', properties: { username: { type: 'string', minLength: 3, maxLength: 20, }, password: { type: 'string', minLength: 8, pattern: '^(?=.*[A-Za-z])(?=.*\\d)[A-Za-z\\d]{8,}$', }, }, }; const result = validateInput(input, schema); expect(result.isValid).toBe(false); expect(result.errors).toHaveLength(2); expect(result.errors[0]).toContain('password'); expect(result.errors[0]).toContain('at least 8'); expect(result.errors[1]).toContain('password'); expect(result.errors[1]).toContain('pattern'); }); it('should validate number constraints', () => { const input = { age: 15, score: 110, }; const schema: ValidationSchema = { type: 'object', properties: { age: { type: 'number', minimum: 18, }, score: { type: 'number', minimum: 0, maximum: 100, }, }, }; const result = validateInput(input, schema); expect(result.isValid).toBe(false); expect(result.errors).toHaveLength(2); expect(result.errors[0]).toContain('age'); expect(result.errors[0]).toContain('greater than or equal to 18'); expect(result.errors[1]).toContain('score'); expect(result.errors[1]).toContain('less than or equal to 100'); }); it('should validate enum values', () => { const input = { status: 'pending', priority: 'urgent', }; const schema: ValidationSchema = { type: 'object', properties: { status: { type: 'string', enum: ['pending', 'active', 'completed'], }, priority: { type: 'string', enum: ['low', 'medium', 'high'], }, }, }; const result = validateInput(input, schema); expect(result.isValid).toBe(false); expect(result.errors).toHaveLength(1); expect(result.errors[0]).toContain('priority'); expect(result.errors[0]).toContain('one of: low, medium, high'); }); }); describe('validateRequest', () => { it('should return null for valid input', () => { const input = { name: 'Test User', email: 'test@example.com', }; const schema: ValidationSchema = { type: 'object', required: ['name', 'email'], properties: { name: { type: 'string' }, email: { type: 'string' }, }, }; const errorFormatter = vi.fn((error, type, details) => ({ error: true, message: error.message, type, details, })); const result = validateRequest(input, schema, errorFormatter); expect(result).toBeNull(); expect(errorFormatter).not.toHaveBeenCalled(); }); it('should return formatted error for invalid input', () => { const input = { name: 'Test User', // missing required email }; const schema: ValidationSchema = { type: 'object', required: ['name', 'email'], properties: { name: { type: 'string' }, email: { type: 'string' }, }, }; const errorFormatter = vi.fn((error, type, details) => ({ error: true, message: error.message, type, details, })); const result = validateRequest(input, schema, errorFormatter); expect(result).not.toBeNull(); expect(errorFormatter).toHaveBeenCalledWith( expect.any(Error), ErrorType.VALIDATION_ERROR, expect.objectContaining({ errors: expect.arrayContaining([expect.stringContaining('email')]), input, }) ); }); }); });

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