Skip to main content
Glama
base-handler.test.tsβ€’10.4 kB
/** * Unit tests for BaseTriggerHandler */ import { describe, it, expect, vi, beforeEach } from 'vitest'; import { BaseTriggerHandler } from '../../../../src/triggers/handlers/base-handler'; import { N8nApiClient } from '../../../../src/services/n8n-api-client'; import { InstanceContext } from '../../../../src/types/instance-context'; import { Workflow } from '../../../../src/types/n8n-api'; import { TriggerType, TriggerResponse, TriggerHandlerCapabilities, BaseTriggerInput } from '../../../../src/triggers/types'; import { z } from 'zod'; // Mock getN8nApiConfig vi.mock('../../../../src/config/n8n-api', () => ({ getN8nApiConfig: vi.fn(() => ({ baseUrl: 'https://env-n8n.example.com/api/v1', apiKey: 'env-api-key', })), })); // Create a concrete implementation for testing class TestHandler extends BaseTriggerHandler { readonly triggerType: TriggerType = 'webhook'; readonly capabilities: TriggerHandlerCapabilities = { requiresActiveWorkflow: true, canPassInputData: true, }; readonly inputSchema = z.object({ workflowId: z.string(), triggerType: z.literal('webhook'), }); async execute( input: BaseTriggerInput, workflow: Workflow ): Promise<TriggerResponse> { return { success: true, triggerType: this.triggerType, workflowId: input.workflowId, data: { test: 'data' }, metadata: { duration: 100 }, }; } } // Create mock client const createMockClient = (): N8nApiClient => ({ getWorkflow: vi.fn(), listWorkflows: vi.fn(), createWorkflow: vi.fn(), updateWorkflow: vi.fn(), deleteWorkflow: vi.fn(), triggerWebhook: vi.fn(), getExecution: vi.fn(), listExecutions: vi.fn(), deleteExecution: vi.fn(), } as unknown as N8nApiClient); describe('BaseTriggerHandler', () => { let mockClient: N8nApiClient; beforeEach(() => { mockClient = createMockClient(); vi.clearAllMocks(); }); describe('constructor', () => { it('should initialize with client only', () => { const handler = new TestHandler(mockClient); expect(handler).toBeDefined(); expect(handler.triggerType).toBe('webhook'); }); it('should initialize with client and context', () => { const context: InstanceContext = { n8nApiUrl: 'https://test.n8n.com/api/v1', n8nApiKey: 'test-key', sessionId: 'test-session', }; const handler = new TestHandler(mockClient, context); expect(handler).toBeDefined(); }); }); describe('validate', () => { it('should validate correct input', () => { const handler = new TestHandler(mockClient); const input = { workflowId: 'workflow-123', triggerType: 'webhook', }; const result = handler.validate(input); expect(result).toEqual(input); }); it('should throw ZodError for invalid input', () => { const handler = new TestHandler(mockClient); const input = { workflowId: 123, // Wrong type triggerType: 'webhook', }; expect(() => handler.validate(input)).toThrow(); }); it('should throw ZodError for missing required fields', () => { const handler = new TestHandler(mockClient); const input = { triggerType: 'webhook', // Missing workflowId }; expect(() => handler.validate(input)).toThrow(); }); }); describe('getBaseUrl', () => { it('should return base URL from context', () => { const context: InstanceContext = { n8nApiUrl: 'https://context.n8n.com/api/v1', n8nApiKey: 'context-key', sessionId: 'test-session', }; const handler = new TestHandler(mockClient, context); const baseUrl = (handler as any).getBaseUrl(); expect(baseUrl).toBe('https://context.n8n.com'); }); it('should strip trailing slash and /api/v1 from context URL', () => { const context: InstanceContext = { n8nApiUrl: 'https://context.n8n.com/api/v1/', n8nApiKey: 'context-key', sessionId: 'test-session', }; const handler = new TestHandler(mockClient, context); const baseUrl = (handler as any).getBaseUrl(); expect(baseUrl).toBe('https://context.n8n.com'); }); it('should return base URL from environment config when no context', () => { const handler = new TestHandler(mockClient); const baseUrl = (handler as any).getBaseUrl(); expect(baseUrl).toBe('https://env-n8n.example.com'); }); it('should prefer context over environment config', () => { const context: InstanceContext = { n8nApiUrl: 'https://context.n8n.com/api/v1', n8nApiKey: 'context-key', sessionId: 'test-session', }; const handler = new TestHandler(mockClient, context); const baseUrl = (handler as any).getBaseUrl(); expect(baseUrl).toBe('https://context.n8n.com'); }); }); describe('getApiKey', () => { it('should return API key from context', () => { const context: InstanceContext = { n8nApiUrl: 'https://context.n8n.com/api/v1', n8nApiKey: 'context-api-key', sessionId: 'test-session', }; const handler = new TestHandler(mockClient, context); const apiKey = (handler as any).getApiKey(); expect(apiKey).toBe('context-api-key'); }); it('should return API key from environment config when no context', () => { const handler = new TestHandler(mockClient); const apiKey = (handler as any).getApiKey(); expect(apiKey).toBe('env-api-key'); }); it('should prefer context over environment config', () => { const context: InstanceContext = { n8nApiUrl: 'https://context.n8n.com/api/v1', n8nApiKey: 'context-key', sessionId: 'test-session', }; const handler = new TestHandler(mockClient, context); const apiKey = (handler as any).getApiKey(); expect(apiKey).toBe('context-key'); }); }); describe('normalizeResponse', () => { it('should create normalized success response', () => { const handler = new TestHandler(mockClient); const input: BaseTriggerInput = { workflowId: 'workflow-123', triggerType: 'webhook', }; const startTime = Date.now() - 150; const result = { data: 'test-result' }; const response = (handler as any).normalizeResponse(result, input, startTime); expect(response.success).toBe(true); expect(response.triggerType).toBe('webhook'); expect(response.workflowId).toBe('workflow-123'); expect(response.data).toEqual(result); expect(response.metadata.duration).toBeGreaterThanOrEqual(150); }); it('should merge extra fields into response', () => { const handler = new TestHandler(mockClient); const input: BaseTriggerInput = { workflowId: 'workflow-123', triggerType: 'webhook', }; const startTime = Date.now(); const result = { data: 'test' }; const extra = { executionId: 'exec-123', status: 200, }; const response = (handler as any).normalizeResponse(result, input, startTime, extra); expect(response.executionId).toBe('exec-123'); expect(response.status).toBe(200); }); it('should calculate duration correctly', () => { const handler = new TestHandler(mockClient); const input: BaseTriggerInput = { workflowId: 'workflow-123', triggerType: 'webhook', }; const startTime = Date.now() - 500; const response = (handler as any).normalizeResponse({}, input, startTime); expect(response.metadata.duration).toBeGreaterThanOrEqual(500); expect(response.metadata.duration).toBeLessThan(1000); }); }); describe('errorResponse', () => { it('should create error response', () => { const handler = new TestHandler(mockClient); const input: BaseTriggerInput = { workflowId: 'workflow-123', triggerType: 'webhook', }; const startTime = Date.now() - 200; const response = (handler as any).errorResponse( input, 'Test error message', startTime ); expect(response.success).toBe(false); expect(response.triggerType).toBe('webhook'); expect(response.workflowId).toBe('workflow-123'); expect(response.error).toBe('Test error message'); expect(response.metadata.duration).toBeGreaterThanOrEqual(200); }); it('should merge extra error details', () => { const handler = new TestHandler(mockClient); const input: BaseTriggerInput = { workflowId: 'workflow-123', triggerType: 'webhook', }; const startTime = Date.now(); const extra = { code: 'ERR_TEST', details: { reason: 'test reason' }, }; const response = (handler as any).errorResponse( input, 'Error', startTime, extra ); expect(response.code).toBe('ERR_TEST'); expect(response.details).toEqual({ reason: 'test reason' }); }); it('should calculate error duration correctly', () => { const handler = new TestHandler(mockClient); const input: BaseTriggerInput = { workflowId: 'workflow-123', triggerType: 'webhook', }; const startTime = Date.now() - 750; const response = (handler as any).errorResponse(input, 'Error', startTime); expect(response.metadata.duration).toBeGreaterThanOrEqual(750); expect(response.metadata.duration).toBeLessThan(1500); }); }); describe('execute', () => { it('should execute successfully', async () => { const handler = new TestHandler(mockClient); const input: BaseTriggerInput = { workflowId: 'workflow-123', triggerType: 'webhook', }; const workflow: Workflow = { id: 'workflow-123', name: 'Test Workflow', active: true, nodes: [], connections: {}, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), settings: {}, staticData: undefined, } as Workflow; const response = await handler.execute(input, workflow); expect(response.success).toBe(true); expect(response.workflowId).toBe('workflow-123'); expect(response.data).toEqual({ test: 'data' }); }); }); });

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/czlonkowski/n8n-mcp'

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