Skip to main content
Glama
UniversalCreateService-validation-errors.test.tsβ€’9.58 kB
/** * Split: UniversalCreateService validation and error handling */ import { describe, it, expect, beforeEach, vi } from 'vitest'; vi.mock('../../src/services/ValidationService.js', () => ({ ValidationService: { truncateSuggestions: vi.fn((s: string[]) => s), validateEmailAddresses: vi.fn(), }, })); vi.mock('../../src/handlers/tool-configs/universal/field-mapper.js', () => ({ mapRecordFields: vi.fn(), validateResourceType: vi.fn(), getFieldSuggestions: vi.fn(), validateFields: vi.fn(), getValidResourceTypes: vi.fn(() => ''), FIELD_MAPPINGS: {}, })); vi.mock('../../src/utils/validation-utils.js', () => ({ validateRecordFields: vi.fn(), })); vi.mock('../../src/utils/attribute-format-helpers.js', () => ({ convertAttributeFormats: vi.fn((t: string, d: any) => d), getFormatErrorHelp: vi.fn( (t: string, f: string, m: string) => `Enhanced: ${m}` ), })); vi.mock('../../src/errors/enhanced-api-errors.js', () => ({ EnhancedApiError: vi .fn() .mockImplementation( ( message: string, statusCode: number, endpoint: string, method: string ) => { const e: any = new Error(message); e.statusCode = statusCode; e.endpoint = endpoint; e.method = method; e.name = 'EnhancedApiError'; return e; } ), ErrorEnhancer: { autoEnhance: vi.fn((e: any) => e) }, ErrorTemplates: { TASK_FIELD_MAPPING: vi.fn( (a: string, b: string) => new Error(`Use ${b} instead of ${a}`) ), }, })); // Mock the create service factory to return a mock service const mockCreateService = { createCompany: vi.fn(), createPerson: vi.fn(), createTask: vi.fn(), createList: vi.fn(), createNote: vi.fn(), createDeal: vi.fn(), updateTask: vi.fn(), }; vi.mock('../../src/services/create/index.js', () => ({ getCreateService: vi.fn(() => mockCreateService), shouldUseMockData: vi.fn(() => true), })); import { UniversalCreateService } from '../../src/services/UniversalCreateService.js'; import { UniversalResourceType } from '../../src/handlers/tool-configs/universal/types.js'; import { getCreateService } from '../../src/services/create/index.js'; import { validateFields, mapRecordFields, getFieldSuggestions, validateResourceType, } from '../../src/handlers/tool-configs/universal/field-mapper.js'; import { validateRecordFields } from '../../src/utils/validation-utils.js'; import { ErrorEnhancer, EnhancedApiError, } from '../../src/errors/enhanced-api-errors.js'; import { getFormatErrorHelp } from '../../src/utils/attribute-format-helpers.js'; describe('UniversalCreateService', () => { beforeEach(() => { vi.clearAllMocks(); delete process.env.ENABLE_ENHANCED_VALIDATION; // Setup default mock returns vi.mocked(validateFields).mockReturnValue({ valid: true, warnings: [], suggestions: [], errors: [], } as any); vi.mocked(mapRecordFields).mockImplementation( (resourceType: string, data: any) => ({ mapped: data, warnings: [], errors: [], }) as any ); }); describe('createRecord - validation/errors', () => { it('should handle field validation errors', async () => { vi.mocked(validateFields).mockReturnValue({ valid: false, warnings: [], suggestions: ['Try using "name" instead'], errors: ['Invalid field: xyz'], } as any); await expect( UniversalCreateService.createRecord({ resource_type: UniversalResourceType.COMPANIES, record_data: { values: { xyz: 'Invalid' } }, }) ).rejects.toThrow('Validation failed for companies'); }); it('should handle field mapping errors', async () => { vi.mocked(mapRecordFields).mockReturnValue({ mapped: {}, warnings: [], errors: ['Mapping error: conflicting fields'], } as any); await expect( UniversalCreateService.createRecord({ resource_type: UniversalResourceType.COMPANIES, record_data: { values: { name: 'Test' } }, }) ).rejects.toThrow('Mapping error: conflicting fields'); }); it('should handle enhanced validation when enabled', async () => { process.env.ENABLE_ENHANCED_VALIDATION = 'true'; vi.mocked(validateRecordFields).mockResolvedValue({ isValid: false, error: 'Enhanced validation failed', } as any); await expect( UniversalCreateService.createRecord({ resource_type: UniversalResourceType.COMPANIES, record_data: { values: { name: 'Test' } }, }) ).rejects.toThrow('Enhanced validation failed'); }); it('should handle company creation with null result', async () => { mockCreateService.createCompany.mockResolvedValue(null as any); await expect( UniversalCreateService.createRecord({ resource_type: UniversalResourceType.COMPANIES, record_data: { values: { name: 'Test Company' } }, }) ).rejects.toThrow( 'Company creation failed: createCompany returned null/undefined' ); }); it('should handle company creation with invalid ID structure', async () => { mockCreateService.createCompany.mockResolvedValue({ values: { name: 'Test Company' }, } as any); await expect( UniversalCreateService.createRecord({ resource_type: UniversalResourceType.COMPANIES, record_data: { values: { name: 'Test Company' } }, }) ).rejects.toThrow('Company creation failed: Invalid record structure'); }); it('should handle attribute not found errors with suggestions', async () => { const error = new Error( 'Cannot find attribute with slug/ID "invalid_field"' ); mockCreateService.createCompany.mockRejectedValue(error); vi.mocked(getFieldSuggestions).mockReturnValue('Did you mean "name"?'); vi.mocked(getFormatErrorHelp).mockReturnValue('Enhanced error message'); await expect( UniversalCreateService.createRecord({ resource_type: UniversalResourceType.COMPANIES, record_data: { values: { name: 'Test Company' } }, }) ).rejects.toThrow('Enhanced error message'); }); it('should handle uniqueness constraint errors', async () => { const error = new Error( 'uniqueness constraint violation for field "name"' ); mockCreateService.createCompany.mockRejectedValue(error); await expect( UniversalCreateService.createRecord({ resource_type: UniversalResourceType.COMPANIES, record_data: { values: { name: 'Duplicate Company' } }, }) ).rejects.toThrow('Uniqueness constraint violation for companies'); }); it('should handle unsupported resource type with correction', async () => { vi.mocked(validateResourceType).mockReturnValue({ valid: false, corrected: UniversalResourceType.COMPANIES, suggestion: 'Did you mean "companies"?', } as any); mockCreateService.createCompany.mockResolvedValue({ id: { record_id: 'comp_123' }, values: { name: 'Test Company' }, } as any); const result = await UniversalCreateService.createRecord({ resource_type: 'company' as any, record_data: { values: { name: 'Test Company' } }, }); expect(result).toBeDefined(); }); it('should handle unsupported resource type without correction', async () => { vi.mocked(validateResourceType).mockReturnValue({ valid: false, corrected: undefined, suggestion: 'Valid resource types are: companies, people, lists', } as any); await expect( UniversalCreateService.createRecord({ resource_type: 'invalid' as any, record_data: { values: { name: 'Test' } }, }) ).rejects.toThrow('Unsupported resource type: invalid'); }); it('should handle task creation errors with enhanced error handling', async () => { const originalError = new Error('Task creation failed'); mockCreateService.createTask.mockRejectedValue(originalError); vi.mocked(ErrorEnhancer.autoEnhance).mockReturnValue( new EnhancedApiError( 'Enhanced task error', 400, '/api/tasks', 'POST' ) as any ); vi.mocked(mapRecordFields).mockReturnValue({ mapped: { content: 'Test Task' }, warnings: [], errors: [], } as any); await expect( UniversalCreateService.createRecord({ resource_type: UniversalResourceType.TASKS, record_data: { values: { content: 'Test Task' } }, }) ).rejects.toThrow('Enhanced task error'); }); it('should handle field validation warnings and suggestions logging', async () => { process.env.E2E_MODE = 'true'; vi.mocked(validateFields).mockReturnValue({ valid: true, warnings: ['Field warning 1', 'Field warning 2'], suggestions: ['Suggestion 1', 'Suggestion 2'], errors: [], } as any); mockCreateService.createCompany.mockResolvedValue({ id: { record_id: 'comp_123' }, values: { name: 'Test Company' }, } as any); const result = await UniversalCreateService.createRecord({ resource_type: UniversalResourceType.COMPANIES, record_data: { values: { name: 'Test Company' } }, }); expect(result).toBeDefined(); }); }); });

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