Skip to main content
Glama

n8n-workflow-builder-mcp

by ifmelate
validate-workflow.test.js8.56 kB
const { describe, it, expect } = require('@jest/globals'); const path = require('path'); const fs = require('fs').promises; const { validateAndNormalizeWorkflow, SimpleNodeTypes } = require('../../dist/validation/workflowValidator.js'); const { loadNodeTypesFromDir } = require('../../dist/validation/nodeTypesLoader.js'); async function readWorkflow(filePath) { const raw = await fs.readFile(filePath, 'utf8'); return JSON.parse(raw); } describe('Workflow validation with scraped node definitions', () => { const version = '1.103.0'; const nodesDir = path.resolve(__dirname, `../../workflow_nodes/${version}`); const workflowPath = path.resolve(__dirname, `../../workflow_data/UNIT_VERSION_MATRIX.json`); it('validates UNIT_VERSION_MATRIX workflow against node definitions (structure only)', async () => { const nodeTypes = await loadNodeTypesFromDir(nodesDir); const wf = await readWorkflow(workflowPath); const report = validateAndNormalizeWorkflow(wf, nodeTypes); expect(report).toBeDefined(); expect(Array.isArray(report.errors)).toBe(true); expect(Array.isArray(report.warnings)).toBe(true); }); }); describe('validate_workflow tool behavior', () => { function makeRegistry(pairs) { const reg = new SimpleNodeTypes(); for (const [name, version] of pairs) { reg.register(name, version, { name, properties: [] }); } return reg; } function simulateValidateWorkflowTool(workflow, nodeTypes) { // Simulate the validate_workflow tool logic const report = validateAndNormalizeWorkflow(workflow, nodeTypes); // Apply the same logic as the modified validate_workflow tool const allErrors = [...report.errors]; // Convert warnings to errors for this tool if (report.warnings.length > 0) { for (const warning of report.warnings) { allErrors.push({ code: warning.code, message: warning.message, nodeName: warning.nodeName, details: warning.details }); } } // Workflow is only OK if there are no errors AND no warnings const validationOk = report.ok && report.warnings.length === 0; return { success: true, validation: { ok: validationOk, errors: allErrors, startNode: report.startNode, originalWarningCount: report.warnings.length, note: report.warnings.length > 0 ? "Warnings have been promoted to errors for validation tool" : undefined } }; } it('treats warnings as errors and sets validation ok to false', () => { const nodeTypes = makeRegistry([ ['dummy.type1', 1], ['dummy.type2', 1], ]); const workflow = { name: 'DUMMY_UNCONNECTED', nodes: [ { id: 'node-abc-123', name: 'A', type: 'dummy.type1', typeVersion: 1, parameters: {} }, { id: 'node-def-456', name: 'B', type: 'dummy.type2', typeVersion: 1, parameters: {} }, ], connections: {}, }; const result = simulateValidateWorkflowTool(workflow, nodeTypes); // Should be successful but validation not ok due to warnings converted to errors expect(result.success).toBe(true); expect(result.validation.ok).toBe(false); // Should have 2 errors (converted from warnings) expect(result.validation.errors.length).toBe(2); // Should track original warning count expect(result.validation.originalWarningCount).toBe(2); // Should have note about promotion expect(result.validation.note).toBe("Warnings have been promoted to errors for validation tool"); // Errors should contain the unconnected node warnings with node IDs const errorCodes = result.validation.errors.map(e => e.code); expect(errorCodes).toEqual(['unconnected_node', 'unconnected_node']); // Check that node IDs are included in the error details expect(result.validation.errors[0].details.nodeId).toBe('node-abc-123'); expect(result.validation.errors[1].details.nodeId).toBe('node-def-456'); // Check that messages include node IDs expect(result.validation.errors[0].message).toContain('node-abc-123'); expect(result.validation.errors[1].message).toContain('node-def-456'); }); it('handles workflows with no warnings correctly', () => { const nodeTypes = makeRegistry([ ['dummy.source', 1], ['dummy.target', 1], ]); const workflow = { name: 'DUMMY_CONNECTED', nodes: [ { id: 'source-123', name: 'Source', type: 'dummy.source', typeVersion: 1, parameters: {} }, { id: 'target-456', name: 'Target', type: 'dummy.target', typeVersion: 1, parameters: {} }, ], connections: { Source: { main: [ [{ node: 'Target', type: 'main', index: 0 }] ] } }, }; const result = simulateValidateWorkflowTool(workflow, nodeTypes); // Should be successful and validation ok expect(result.success).toBe(true); expect(result.validation.ok).toBe(true); // Should have no errors expect(result.validation.errors.length).toBe(0); // Should have zero original warnings expect(result.validation.originalWarningCount).toBe(0); // Should have no note expect(result.validation.note).toBeUndefined(); }); it('combines real errors with promoted warnings', () => { const nodeTypes = makeRegistry([ ['dummy.type1', 1], // Don't register dummy.type2 to create a real error ]); const workflow = { name: 'DUMMY_MIXED_ISSUES', nodes: [ { id: 'node-abc-123', name: 'A', type: 'dummy.type1', typeVersion: 1, parameters: {} }, { id: 'node-def-456', name: 'B', type: 'dummy.type2', // This will cause a real error typeVersion: 1, parameters: {} }, ], connections: {}, }; const result = simulateValidateWorkflowTool(workflow, nodeTypes); // Should be successful but validation not ok expect(result.success).toBe(true); expect(result.validation.ok).toBe(false); // Should have multiple errors (both real errors and promoted warnings) expect(result.validation.errors.length).toBeGreaterThan(0); // Should have note about promotion since there are warnings expect(result.validation.originalWarningCount).toBeGreaterThan(0); expect(result.validation.note).toBe("Warnings have been promoted to errors for validation tool"); }); it('removes warnings field from response', () => { const nodeTypes = makeRegistry([ ['dummy.type1', 1], ]); const workflow = { name: 'DUMMY_UNCONNECTED', nodes: [ { id: 'node-abc-123', name: 'A', type: 'dummy.type1', typeVersion: 1, parameters: {} }, ], connections: {}, }; const result = simulateValidateWorkflowTool(workflow, nodeTypes); // Should not have warnings field at all expect(result.validation.hasOwnProperty('warnings')).toBe(false); // But should have errors field expect(result.validation.hasOwnProperty('errors')).toBe(true); }); });

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/ifmelate/n8n-workflow-builder-mcp'

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