Skip to main content
Glama

n8n-MCP

by 88-888
execution-processor.test.ts18 kB
/** * Execution Processor Service Tests * * Comprehensive test coverage for execution filtering and processing */ import { describe, it, expect } from 'vitest'; import { generatePreview, filterExecutionData, processExecution, } from '../../../src/services/execution-processor'; import { Execution, ExecutionStatus, ExecutionFilterOptions, } from '../../../src/types/n8n-api'; /** * Test data factories */ function createMockExecution(options: { id?: string; status?: ExecutionStatus; nodeData?: Record<string, any>; hasError?: boolean; }): Execution { const { id = 'test-exec-1', status = ExecutionStatus.SUCCESS, nodeData = {}, hasError = false } = options; return { id, workflowId: 'workflow-1', status, mode: 'manual', finished: true, startedAt: '2024-01-01T10:00:00.000Z', stoppedAt: '2024-01-01T10:00:05.000Z', data: { resultData: { runData: nodeData, error: hasError ? { message: 'Test error' } : undefined, }, }, }; } function createNodeData(itemCount: number, includeError = false) { const items = Array.from({ length: itemCount }, (_, i) => ({ json: { id: i + 1, name: `Item ${i + 1}`, value: Math.random() * 100, nested: { field1: `value${i}`, field2: true, }, }, })); return [ { startTime: Date.now(), executionTime: 123, data: { main: [items], }, error: includeError ? { message: 'Node error' } : undefined, }, ]; } /** * Preview Mode Tests */ describe('ExecutionProcessor - Preview Mode', () => { it('should generate preview for empty execution', () => { const execution = createMockExecution({ nodeData: {} }); const { preview, recommendation } = generatePreview(execution); expect(preview.totalNodes).toBe(0); expect(preview.executedNodes).toBe(0); expect(preview.estimatedSizeKB).toBe(0); expect(recommendation.canFetchFull).toBe(true); expect(recommendation.suggestedMode).toBe('full'); // Empty execution is safe to fetch in full }); it('should generate preview with accurate item counts', () => { const execution = createMockExecution({ nodeData: { 'HTTP Request': createNodeData(50), 'Filter': createNodeData(12), }, }); const { preview } = generatePreview(execution); expect(preview.totalNodes).toBe(2); expect(preview.executedNodes).toBe(2); expect(preview.nodes['HTTP Request'].itemCounts.output).toBe(50); expect(preview.nodes['Filter'].itemCounts.output).toBe(12); }); it('should extract data structure from nodes', () => { const execution = createMockExecution({ nodeData: { 'HTTP Request': createNodeData(5), }, }); const { preview } = generatePreview(execution); const structure = preview.nodes['HTTP Request'].dataStructure; expect(structure).toHaveProperty('json'); expect(structure.json).toHaveProperty('id'); expect(structure.json).toHaveProperty('name'); expect(structure.json).toHaveProperty('nested'); expect(structure.json.id).toBe('number'); expect(structure.json.name).toBe('string'); expect(typeof structure.json.nested).toBe('object'); }); it('should estimate data size', () => { const execution = createMockExecution({ nodeData: { 'HTTP Request': createNodeData(50), }, }); const { preview } = generatePreview(execution); expect(preview.estimatedSizeKB).toBeGreaterThan(0); expect(preview.nodes['HTTP Request'].estimatedSizeKB).toBeGreaterThan(0); }); it('should detect error status in nodes', () => { const execution = createMockExecution({ nodeData: { 'HTTP Request': createNodeData(5, true), }, }); const { preview } = generatePreview(execution); expect(preview.nodes['HTTP Request'].status).toBe('error'); expect(preview.nodes['HTTP Request'].error).toBeDefined(); }); it('should recommend full mode for small datasets', () => { const execution = createMockExecution({ nodeData: { 'HTTP Request': createNodeData(5), }, }); const { recommendation } = generatePreview(execution); expect(recommendation.canFetchFull).toBe(true); expect(recommendation.suggestedMode).toBe('full'); }); it('should recommend filtered mode for large datasets', () => { const execution = createMockExecution({ nodeData: { 'HTTP Request': createNodeData(100), }, }); const { recommendation } = generatePreview(execution); expect(recommendation.canFetchFull).toBe(false); expect(recommendation.suggestedMode).toBe('filtered'); expect(recommendation.suggestedItemsLimit).toBeGreaterThan(0); }); it('should recommend summary mode for moderate datasets', () => { const execution = createMockExecution({ nodeData: { 'HTTP Request': createNodeData(30), }, }); const { recommendation } = generatePreview(execution); expect(recommendation.canFetchFull).toBe(false); expect(recommendation.suggestedMode).toBe('summary'); }); }); /** * Filtering Mode Tests */ describe('ExecutionProcessor - Filtering', () => { it('should filter by node names', () => { const execution = createMockExecution({ nodeData: { 'HTTP Request': createNodeData(10), 'Filter': createNodeData(5), 'Set': createNodeData(3), }, }); const options: ExecutionFilterOptions = { mode: 'filtered', nodeNames: ['HTTP Request', 'Filter'], }; const result = filterExecutionData(execution, options); expect(result.nodes).toHaveProperty('HTTP Request'); expect(result.nodes).toHaveProperty('Filter'); expect(result.nodes).not.toHaveProperty('Set'); expect(result.summary?.executedNodes).toBe(2); }); it('should handle non-existent node names gracefully', () => { const execution = createMockExecution({ nodeData: { 'HTTP Request': createNodeData(10), }, }); const options: ExecutionFilterOptions = { mode: 'filtered', nodeNames: ['NonExistent'], }; const result = filterExecutionData(execution, options); expect(Object.keys(result.nodes || {})).toHaveLength(0); expect(result.summary?.executedNodes).toBe(0); }); it('should limit items to 0 (structure only)', () => { const execution = createMockExecution({ nodeData: { 'HTTP Request': createNodeData(50), }, }); const options: ExecutionFilterOptions = { mode: 'filtered', itemsLimit: 0, }; const result = filterExecutionData(execution, options); const nodeData = result.nodes?.['HTTP Request']; expect(nodeData?.data?.metadata.itemsShown).toBe(0); expect(nodeData?.data?.metadata.truncated).toBe(true); expect(nodeData?.data?.metadata.totalItems).toBe(50); // Check that we have structure but no actual values const output = nodeData?.data?.output?.[0]?.[0]; expect(output).toBeDefined(); expect(typeof output).toBe('object'); }); it('should limit items to 2 (default)', () => { const execution = createMockExecution({ nodeData: { 'HTTP Request': createNodeData(50), }, }); const options: ExecutionFilterOptions = { mode: 'summary', }; const result = filterExecutionData(execution, options); const nodeData = result.nodes?.['HTTP Request']; expect(nodeData?.data?.metadata.itemsShown).toBe(2); expect(nodeData?.data?.metadata.totalItems).toBe(50); expect(nodeData?.data?.metadata.truncated).toBe(true); expect(nodeData?.data?.output?.[0]).toHaveLength(2); }); it('should limit items to custom value', () => { const execution = createMockExecution({ nodeData: { 'HTTP Request': createNodeData(50), }, }); const options: ExecutionFilterOptions = { mode: 'filtered', itemsLimit: 5, }; const result = filterExecutionData(execution, options); const nodeData = result.nodes?.['HTTP Request']; expect(nodeData?.data?.metadata.itemsShown).toBe(5); expect(nodeData?.data?.metadata.truncated).toBe(true); expect(nodeData?.data?.output?.[0]).toHaveLength(5); }); it('should not truncate when itemsLimit is -1 (unlimited)', () => { const execution = createMockExecution({ nodeData: { 'HTTP Request': createNodeData(50), }, }); const options: ExecutionFilterOptions = { mode: 'filtered', itemsLimit: -1, }; const result = filterExecutionData(execution, options); const nodeData = result.nodes?.['HTTP Request']; expect(nodeData?.data?.metadata.itemsShown).toBe(50); expect(nodeData?.data?.metadata.totalItems).toBe(50); expect(nodeData?.data?.metadata.truncated).toBe(false); }); it('should not truncate when items are less than limit', () => { const execution = createMockExecution({ nodeData: { 'HTTP Request': createNodeData(3), }, }); const options: ExecutionFilterOptions = { mode: 'filtered', itemsLimit: 5, }; const result = filterExecutionData(execution, options); const nodeData = result.nodes?.['HTTP Request']; expect(nodeData?.data?.metadata.itemsShown).toBe(3); expect(nodeData?.data?.metadata.truncated).toBe(false); }); it('should include input data when requested', () => { const execution = createMockExecution({ nodeData: { 'HTTP Request': [ { startTime: Date.now(), executionTime: 100, inputData: [[{ json: { input: 'test' } }]], data: { main: [[{ json: { output: 'result' } }]], }, }, ], }, }); const options: ExecutionFilterOptions = { mode: 'filtered', includeInputData: true, }; const result = filterExecutionData(execution, options); const nodeData = result.nodes?.['HTTP Request']; expect(nodeData?.data?.input).toBeDefined(); expect(nodeData?.data?.input?.[0]?.[0]?.json?.input).toBe('test'); }); it('should not include input data by default', () => { const execution = createMockExecution({ nodeData: { 'HTTP Request': [ { startTime: Date.now(), executionTime: 100, inputData: [[{ json: { input: 'test' } }]], data: { main: [[{ json: { output: 'result' } }]], }, }, ], }, }); const options: ExecutionFilterOptions = { mode: 'filtered', }; const result = filterExecutionData(execution, options); const nodeData = result.nodes?.['HTTP Request']; expect(nodeData?.data?.input).toBeUndefined(); }); }); /** * Mode Tests */ describe('ExecutionProcessor - Modes', () => { it('should handle preview mode', () => { const execution = createMockExecution({ nodeData: { 'HTTP Request': createNodeData(50), }, }); const result = filterExecutionData(execution, { mode: 'preview' }); expect(result.mode).toBe('preview'); expect(result.preview).toBeDefined(); expect(result.recommendation).toBeDefined(); expect(result.nodes).toBeUndefined(); }); it('should handle summary mode', () => { const execution = createMockExecution({ nodeData: { 'HTTP Request': createNodeData(50), }, }); const result = filterExecutionData(execution, { mode: 'summary' }); expect(result.mode).toBe('summary'); expect(result.summary).toBeDefined(); expect(result.nodes).toBeDefined(); expect(result.nodes?.['HTTP Request']?.data?.metadata.itemsShown).toBe(2); }); it('should handle filtered mode', () => { const execution = createMockExecution({ nodeData: { 'HTTP Request': createNodeData(50), }, }); const result = filterExecutionData(execution, { mode: 'filtered', itemsLimit: 5, }); expect(result.mode).toBe('filtered'); expect(result.summary).toBeDefined(); expect(result.nodes?.['HTTP Request']?.data?.metadata.itemsShown).toBe(5); }); it('should handle full mode', () => { const execution = createMockExecution({ nodeData: { 'HTTP Request': createNodeData(50), }, }); const result = filterExecutionData(execution, { mode: 'full' }); expect(result.mode).toBe('full'); expect(result.nodes?.['HTTP Request']?.data?.metadata.itemsShown).toBe(50); expect(result.nodes?.['HTTP Request']?.data?.metadata.truncated).toBe(false); }); }); /** * Edge Cases */ describe('ExecutionProcessor - Edge Cases', () => { it('should handle execution with no data', () => { const execution: Execution = { id: 'test-1', workflowId: 'workflow-1', status: ExecutionStatus.SUCCESS, mode: 'manual', finished: true, startedAt: '2024-01-01T10:00:00.000Z', stoppedAt: '2024-01-01T10:00:05.000Z', }; const result = filterExecutionData(execution, { mode: 'summary' }); expect(result.summary?.totalNodes).toBe(0); expect(result.summary?.executedNodes).toBe(0); }); it('should handle execution with error', () => { const execution = createMockExecution({ nodeData: { 'HTTP Request': createNodeData(5), }, hasError: true, }); const result = filterExecutionData(execution, { mode: 'summary' }); expect(result.error).toBeDefined(); }); it('should handle empty node data arrays', () => { const execution = createMockExecution({ nodeData: { 'HTTP Request': [], }, }); const result = filterExecutionData(execution, { mode: 'summary' }); expect(result.nodes?.['HTTP Request']).toBeDefined(); expect(result.nodes?.['HTTP Request'].itemsOutput).toBe(0); }); it('should handle nested data structures', () => { const execution = createMockExecution({ nodeData: { 'HTTP Request': [ { startTime: Date.now(), executionTime: 100, data: { main: [[{ json: { deeply: { nested: { structure: { value: 'test', array: [1, 2, 3], }, }, }, }, }]], }, }, ], }, }); const { preview } = generatePreview(execution); const structure = preview.nodes['HTTP Request'].dataStructure; expect(structure.json.deeply).toBeDefined(); expect(typeof structure.json.deeply).toBe('object'); }); it('should calculate duration correctly', () => { const execution = createMockExecution({ nodeData: { 'HTTP Request': createNodeData(5), }, }); const result = filterExecutionData(execution, { mode: 'summary' }); expect(result.duration).toBe(5000); // 5 seconds }); it('should handle execution without stop time', () => { const execution: Execution = { id: 'test-1', workflowId: 'workflow-1', status: ExecutionStatus.WAITING, mode: 'manual', finished: false, startedAt: '2024-01-01T10:00:00.000Z', data: { resultData: { runData: {}, }, }, }; const result = filterExecutionData(execution, { mode: 'summary' }); expect(result.duration).toBeUndefined(); expect(result.finished).toBe(false); }); }); /** * processExecution Tests */ describe('ExecutionProcessor - processExecution', () => { it('should return original execution when no options provided', () => { const execution = createMockExecution({ nodeData: { 'HTTP Request': createNodeData(5), }, }); const result = processExecution(execution, {}); expect(result).toBe(execution); }); it('should process when mode is specified', () => { const execution = createMockExecution({ nodeData: { 'HTTP Request': createNodeData(5), }, }); const result = processExecution(execution, { mode: 'preview' }); expect(result).not.toBe(execution); expect((result as any).mode).toBe('preview'); }); it('should process when filtering options are provided', () => { const execution = createMockExecution({ nodeData: { 'HTTP Request': createNodeData(5), 'Filter': createNodeData(3), }, }); const result = processExecution(execution, { nodeNames: ['HTTP Request'] }); expect(result).not.toBe(execution); expect((result as any).nodes).toHaveProperty('HTTP Request'); expect((result as any).nodes).not.toHaveProperty('Filter'); }); }); /** * Summary Statistics Tests */ describe('ExecutionProcessor - Summary Statistics', () => { it('should calculate hasMoreData correctly', () => { const execution = createMockExecution({ nodeData: { 'HTTP Request': createNodeData(50), }, }); const result = filterExecutionData(execution, { mode: 'summary', itemsLimit: 2, }); expect(result.summary?.hasMoreData).toBe(true); }); it('should set hasMoreData to false when all data is included', () => { const execution = createMockExecution({ nodeData: { 'HTTP Request': createNodeData(2), }, }); const result = filterExecutionData(execution, { mode: 'summary', itemsLimit: 5, }); expect(result.summary?.hasMoreData).toBe(false); }); it('should count total items correctly across multiple nodes', () => { const execution = createMockExecution({ nodeData: { 'HTTP Request': createNodeData(10), 'Filter': createNodeData(5), 'Set': createNodeData(3), }, }); const result = filterExecutionData(execution, { mode: 'summary' }); expect(result.summary?.totalItems).toBe(18); }); });

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

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