Skip to main content
Glama
handlers-deploy-template.test.tsβ€’8.77 kB
/** * Unit tests for handleDeployTemplate handler - input validation and schema tests */ import { describe, it, expect } from 'vitest'; import { z } from 'zod'; // Test the schema directly without needing full API mocking const deployTemplateSchema = z.object({ templateId: z.number().positive().int(), name: z.string().optional(), autoUpgradeVersions: z.boolean().default(true), autoFix: z.boolean().default(true), stripCredentials: z.boolean().default(true) }); describe('handleDeployTemplate Schema Validation', () => { describe('Input Schema', () => { it('should require templateId as a positive integer', () => { // Valid input const validResult = deployTemplateSchema.safeParse({ templateId: 123 }); expect(validResult.success).toBe(true); // Invalid: missing templateId const missingResult = deployTemplateSchema.safeParse({}); expect(missingResult.success).toBe(false); // Invalid: templateId as string const stringResult = deployTemplateSchema.safeParse({ templateId: '123' }); expect(stringResult.success).toBe(false); // Invalid: negative templateId const negativeResult = deployTemplateSchema.safeParse({ templateId: -1 }); expect(negativeResult.success).toBe(false); // Invalid: zero templateId const zeroResult = deployTemplateSchema.safeParse({ templateId: 0 }); expect(zeroResult.success).toBe(false); // Invalid: decimal templateId const decimalResult = deployTemplateSchema.safeParse({ templateId: 123.5 }); expect(decimalResult.success).toBe(false); }); it('should accept optional name parameter', () => { const withName = deployTemplateSchema.safeParse({ templateId: 123, name: 'Custom Name' }); expect(withName.success).toBe(true); if (withName.success) { expect(withName.data.name).toBe('Custom Name'); } const withoutName = deployTemplateSchema.safeParse({ templateId: 123 }); expect(withoutName.success).toBe(true); if (withoutName.success) { expect(withoutName.data.name).toBeUndefined(); } }); it('should default autoUpgradeVersions to true', () => { const result = deployTemplateSchema.safeParse({ templateId: 123 }); expect(result.success).toBe(true); if (result.success) { expect(result.data.autoUpgradeVersions).toBe(true); } }); it('should allow setting autoUpgradeVersions to false', () => { const result = deployTemplateSchema.safeParse({ templateId: 123, autoUpgradeVersions: false }); expect(result.success).toBe(true); if (result.success) { expect(result.data.autoUpgradeVersions).toBe(false); } }); it('should default autoFix to true', () => { const result = deployTemplateSchema.safeParse({ templateId: 123 }); expect(result.success).toBe(true); if (result.success) { expect(result.data.autoFix).toBe(true); } }); it('should default stripCredentials to true', () => { const result = deployTemplateSchema.safeParse({ templateId: 123 }); expect(result.success).toBe(true); if (result.success) { expect(result.data.stripCredentials).toBe(true); } }); it('should accept all parameters together', () => { const result = deployTemplateSchema.safeParse({ templateId: 2776, name: 'My Deployed Workflow', autoUpgradeVersions: false, autoFix: false, stripCredentials: false }); expect(result.success).toBe(true); if (result.success) { expect(result.data.templateId).toBe(2776); expect(result.data.name).toBe('My Deployed Workflow'); expect(result.data.autoUpgradeVersions).toBe(false); expect(result.data.autoFix).toBe(false); expect(result.data.stripCredentials).toBe(false); } }); }); }); describe('handleDeployTemplate Helper Functions', () => { describe('Credential Extraction Logic', () => { it('should extract credential types from node credentials object', () => { const nodes = [ { id: 'node-1', name: 'Slack', type: 'n8n-nodes-base.slack', credentials: { slackApi: { id: 'cred-1', name: 'My Slack' } } }, { id: 'node-2', name: 'Google Sheets', type: 'n8n-nodes-base.googleSheets', credentials: { googleSheetsOAuth2Api: { id: 'cred-2', name: 'My Google' } } }, { id: 'node-3', name: 'Set', type: 'n8n-nodes-base.set' // No credentials } ]; // Simulate the credential extraction logic from the handler const requiredCredentials: Array<{ nodeType: string; nodeName: string; credentialType: string; }> = []; for (const node of nodes) { if (node.credentials && typeof node.credentials === 'object') { for (const [credType] of Object.entries(node.credentials)) { requiredCredentials.push({ nodeType: node.type, nodeName: node.name, credentialType: credType }); } } } expect(requiredCredentials).toHaveLength(2); expect(requiredCredentials[0]).toEqual({ nodeType: 'n8n-nodes-base.slack', nodeName: 'Slack', credentialType: 'slackApi' }); expect(requiredCredentials[1]).toEqual({ nodeType: 'n8n-nodes-base.googleSheets', nodeName: 'Google Sheets', credentialType: 'googleSheetsOAuth2Api' }); }); }); describe('Credential Stripping Logic', () => { it('should remove credentials property from nodes', () => { const nodes = [ { id: 'node-1', name: 'Slack', type: 'n8n-nodes-base.slack', typeVersion: 2, position: [250, 300], parameters: { channel: '#general' }, credentials: { slackApi: { id: 'cred-1', name: 'My Slack' } } } ]; // Simulate the credential stripping logic from the handler const strippedNodes = nodes.map((node: any) => { const { credentials, ...rest } = node; return rest; }); expect(strippedNodes[0].credentials).toBeUndefined(); expect(strippedNodes[0].id).toBe('node-1'); expect(strippedNodes[0].name).toBe('Slack'); expect(strippedNodes[0].parameters).toEqual({ channel: '#general' }); }); }); describe('Trigger Type Detection Logic', () => { it('should identify trigger nodes', () => { const testCases = [ { type: 'n8n-nodes-base.scheduleTrigger', expected: 'scheduleTrigger' }, { type: 'n8n-nodes-base.webhook', expected: 'webhook' }, { type: 'n8n-nodes-base.emailReadImapTrigger', expected: 'emailReadImapTrigger' }, { type: 'n8n-nodes-base.googleDriveTrigger', expected: 'googleDriveTrigger' } ]; for (const { type, expected } of testCases) { const nodes = [{ type, name: 'Trigger' }]; // Simulate the trigger detection logic from the handler const triggerNode = nodes.find((n: any) => n.type?.includes('Trigger') || n.type?.includes('webhook') || n.type === 'n8n-nodes-base.webhook' ); const triggerType = triggerNode?.type?.split('.').pop() || 'manual'; expect(triggerType).toBe(expected); } }); it('should return manual for workflows without trigger', () => { const nodes = [ { type: 'n8n-nodes-base.set', name: 'Set' }, { type: 'n8n-nodes-base.httpRequest', name: 'HTTP Request' } ]; const triggerNode = nodes.find((n: any) => n.type?.includes('Trigger') || n.type?.includes('webhook') || n.type === 'n8n-nodes-base.webhook' ); const triggerType = triggerNode?.type?.split('.').pop() || 'manual'; expect(triggerType).toBe('manual'); }); }); }); describe('Tool Definition Validation', () => { it('should have correct tool name', () => { // This tests that the tool is properly exported const toolName = 'n8n_deploy_template'; expect(toolName).toBe('n8n_deploy_template'); }); it('should have required parameter templateId in schema', () => { // Validate that the schema correctly requires templateId const validResult = deployTemplateSchema.safeParse({ templateId: 123 }); const invalidResult = deployTemplateSchema.safeParse({}); expect(validResult.success).toBe(true); expect(invalidResult.success).toBe(false); }); });

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