Skip to main content
Glama
create-services.contract.test.ts8.61 kB
/** * Contract Tests for Create Services * * Ensures both AttioCreateService and MockCreateService return identical shapes * and behave consistently across different environments. */ import { describe, it, expect, beforeEach, vi } from 'vitest'; import { AttioCreateService } from '../../../../src/services/create/attio-create.service.js'; import { MockCreateService } from '../../../../src/services/create/mock-create.service.js'; import type { CreateService } from '../../../../src/services/create/types.js'; describe('Create Services Contract', () => { let attioService: CreateService; let mockService: CreateService; beforeEach(() => { // Mock environment for AttioCreateService to prevent real API calls vi.stubEnv('ATTIO_API_KEY', 'test-api-key'); vi.stubEnv('USE_MOCK_DATA', 'false'); attioService = new AttioCreateService(); mockService = new MockCreateService(); }); describe('createCompany contract', () => { const testCompanyData = { name: 'Test Company', domain: 'test.com', industry: 'Technology', description: 'A test company', }; it('should return records with identical structure', async () => { const mockResult = await mockService.createCompany(testCompanyData); // Both services should return AttioRecord with same structure expect(mockResult).toHaveProperty('id'); expect(mockResult.id).toHaveProperty('record_id'); expect(mockResult.id).toHaveProperty('object_id', 'companies'); expect(mockResult.id).toHaveProperty('workspace_id'); expect(mockResult).toHaveProperty('values'); expect(mockResult.values).toHaveProperty('name'); expect(mockResult.values).toHaveProperty('domains'); expect(mockResult).toHaveProperty('created_at'); expect(mockResult).toHaveProperty('updated_at'); // ID should be valid UUID format expect(mockResult.id.record_id).toMatch( /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i ); // Timestamps should be ISO strings expect(() => new Date(mockResult.created_at as string)).not.toThrow(); expect(() => new Date(mockResult.updated_at as string)).not.toThrow(); }); }); describe('createPerson contract', () => { const testPersonData = { name: 'John Doe', email: 'john.doe@test.com', }; it('should return records with identical structure', async () => { const mockResult = await mockService.createPerson(testPersonData); // Both services should return AttioRecord with same structure expect(mockResult).toHaveProperty('id'); expect(mockResult.id).toHaveProperty('record_id'); expect(mockResult.id).toHaveProperty('object_id', 'people'); expect(mockResult.id).toHaveProperty('workspace_id'); expect(mockResult).toHaveProperty('values'); expect(mockResult.values).toHaveProperty('name'); expect(mockResult.values).toHaveProperty('email_addresses'); expect(mockResult).toHaveProperty('created_at'); expect(mockResult).toHaveProperty('updated_at'); // ID should be valid UUID format expect(mockResult.id.record_id).toMatch( /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i ); // Timestamps should be ISO strings expect(() => new Date(mockResult.created_at as string)).not.toThrow(); expect(() => new Date(mockResult.updated_at as string)).not.toThrow(); }); }); describe('createTask contract', () => { const testTaskData = { content: 'Test task', status: 'pending', assigneeId: '12345678-1234-4000-8000-123456789012', due_date: '2024-12-31', }; it('should return records with identical structure (Issue #480 compatibility)', async () => { const mockResult = await mockService.createTask(testTaskData); // Both services should return AttioRecord with same structure expect(mockResult).toHaveProperty('id'); expect(mockResult.id).toHaveProperty('record_id'); expect(mockResult.id).toHaveProperty('task_id'); // Issue #480 expect(mockResult.id).toHaveProperty('object_id', 'tasks'); expect(mockResult.id).toHaveProperty('workspace_id'); expect(mockResult).toHaveProperty('values'); expect(mockResult.values).toHaveProperty('content'); expect(mockResult.values).toHaveProperty('title'); // Issue #480: Dual field support // Flat field compatibility (Issue #480) expect(mockResult).toHaveProperty('content'); expect(mockResult).toHaveProperty('title'); expect(mockResult).toHaveProperty('status'); expect(mockResult).toHaveProperty('assignees'); expect(mockResult).toHaveProperty('created_at'); expect(mockResult).toHaveProperty('updated_at'); // task_id should equal record_id (Issue #480) expect(mockResult.id.task_id).toBe(mockResult.id.record_id); }); }); describe('updateTask contract', () => { const taskId = '12345678-1234-4000-8000-123456789012'; const updateData = { content: 'Updated task', status: 'completed', assigneeId: '87654321-4321-4000-8000-210987654321', }; it('should return records with identical structure', async () => { const mockResult = await mockService.updateTask(taskId, updateData); // Should maintain same structure as createTask expect(mockResult).toHaveProperty('id'); expect(mockResult.id).toHaveProperty('record_id', taskId); expect(mockResult.id).toHaveProperty('task_id', taskId); expect(mockResult.id).toHaveProperty('object_id', 'tasks'); expect(mockResult).toHaveProperty('values'); expect(mockResult.values).toHaveProperty('content'); expect(mockResult.values).toHaveProperty('title'); // Flat field compatibility expect(mockResult).toHaveProperty('content'); expect(mockResult).toHaveProperty('title'); expect(mockResult).toHaveProperty('status'); expect(mockResult).toHaveProperty('created_at'); expect(mockResult).toHaveProperty('updated_at'); }); it('should validate task ID format', async () => { await expect( mockService.updateTask('invalid-id', updateData) ).rejects.toThrow('Task not found: invalid-id'); }); }); describe('createNote contract', () => { const testNoteData = { resource_type: 'companies', record_id: '12345678-1234-4000-8000-123456789012', title: 'Test Note', content: 'This is a test note', format: 'plaintext', }; it('should return notes with identical structure', async () => { const mockResult = await mockService.createNote(testNoteData); expect(mockResult).toHaveProperty('id'); expect(mockResult.id).toHaveProperty('workspace_id'); expect(mockResult.id).toHaveProperty('note_id'); expect(mockResult.id).toHaveProperty('record_id'); expect(mockResult).toHaveProperty( 'parent_object', testNoteData.resource_type ); expect(mockResult).toHaveProperty('parent_record_id'); expect(mockResult).toHaveProperty('title', testNoteData.title); expect(mockResult).toHaveProperty('content', testNoteData.content); expect(mockResult).toHaveProperty('format', testNoteData.format); expect(mockResult).toHaveProperty('created_at'); }); it('should validate required parameters', async () => { const incompleteData = { resource_type: 'companies' }; await expect( mockService.createNote(incompleteData as any) ).rejects.toThrow('missing required parameter'); }); it('should validate record_id format', async () => { const invalidData = { ...testNoteData, record_id: 'invalid-record-id-12345', }; await expect(mockService.createNote(invalidData)).rejects.toThrow( 'record not found' ); }); }); describe('listNotes contract', () => { it('should return consistent empty array for both services', async () => { const mockResult = await mockService.listNotes({}); expect(Array.isArray(mockResult)).toBe(true); expect(mockResult).toHaveLength(0); }); }); describe('Error handling contract', () => { it('should throw similar error types for validation failures', async () => { // Both services should throw Error for validation failures await expect(mockService.updateTask('invalid', {})).rejects.toThrow( Error ); await expect(mockService.createNote({} as any)).rejects.toThrow(Error); }); }); });

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