mirror of
https://github.com/czlonkowski/n8n-mcp.git
synced 2026-02-06 21:43:07 +00:00
fix: n8n_test_workflow webhookId resolution and form handling (v2.28.2) (#462)
This commit is contained in:
committed by
GitHub
parent
3188d209b7
commit
ef9b6f6341
@@ -8,6 +8,7 @@ import { InstanceContext } from '../../../../src/types/instance-context';
|
||||
import { Workflow } from '../../../../src/types/n8n-api';
|
||||
import { DetectedTrigger } from '../../../../src/triggers/types';
|
||||
import axios from 'axios';
|
||||
import FormData from 'form-data';
|
||||
|
||||
// Mock getN8nApiConfig
|
||||
vi.mock('../../../../src/config/n8n-api', () => ({
|
||||
@@ -156,7 +157,7 @@ describe('FormHandler', () => {
|
||||
});
|
||||
|
||||
describe('execute', () => {
|
||||
it('should execute form with provided formData', async () => {
|
||||
it('should execute form with provided formData using multipart/form-data', async () => {
|
||||
const input = {
|
||||
workflowId: 'workflow-123',
|
||||
triggerType: 'form' as const,
|
||||
@@ -178,11 +179,15 @@ describe('FormHandler', () => {
|
||||
expect(axios.request).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
method: 'POST',
|
||||
data: {
|
||||
name: 'Jane Doe',
|
||||
email: 'jane@example.com',
|
||||
message: 'Hello',
|
||||
},
|
||||
})
|
||||
);
|
||||
// Verify FormData is used
|
||||
const config = vi.mocked(axios.request).mock.calls[0][0];
|
||||
expect(config.data).toBeInstanceOf(FormData);
|
||||
// Verify multipart/form-data content type is set via FormData headers
|
||||
expect(config.headers).toEqual(
|
||||
expect.objectContaining({
|
||||
'content-type': expect.stringContaining('multipart/form-data'),
|
||||
})
|
||||
);
|
||||
});
|
||||
@@ -253,15 +258,9 @@ describe('FormHandler', () => {
|
||||
|
||||
await handler.execute(input, workflow, triggerInfo);
|
||||
|
||||
expect(axios.request).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
data: {
|
||||
field1: 'from data',
|
||||
field2: 'from formData',
|
||||
field3: 'from formData',
|
||||
},
|
||||
})
|
||||
);
|
||||
// Verify FormData is used and contains merged data
|
||||
const config = vi.mocked(axios.request).mock.calls[0][0];
|
||||
expect(config.data).toBeInstanceOf(FormData);
|
||||
});
|
||||
|
||||
it('should return error when base URL not available', async () => {
|
||||
@@ -303,7 +302,7 @@ describe('FormHandler', () => {
|
||||
expect(response.error).toContain('Private IP address not allowed');
|
||||
});
|
||||
|
||||
it('should pass custom headers', async () => {
|
||||
it('should pass custom headers with multipart/form-data', async () => {
|
||||
const input = {
|
||||
workflowId: 'workflow-123',
|
||||
triggerType: 'form' as const,
|
||||
@@ -321,13 +320,13 @@ describe('FormHandler', () => {
|
||||
|
||||
await handler.execute(input, workflow, triggerInfo);
|
||||
|
||||
expect(axios.request).toHaveBeenCalledWith(
|
||||
const config = vi.mocked(axios.request).mock.calls[0][0];
|
||||
expect(config.headers).toEqual(
|
||||
expect.objectContaining({
|
||||
headers: expect.objectContaining({
|
||||
'X-Custom-Header': 'custom-value',
|
||||
'Authorization': 'Bearer token',
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
'X-Custom-Header': 'custom-value',
|
||||
'Authorization': 'Bearer token',
|
||||
// FormData sets multipart/form-data with boundary
|
||||
'content-type': expect.stringContaining('multipart/form-data'),
|
||||
})
|
||||
);
|
||||
});
|
||||
@@ -466,10 +465,15 @@ describe('FormHandler', () => {
|
||||
|
||||
expect(response.success).toBe(false);
|
||||
expect(response.executionId).toBe('exec-111');
|
||||
expect(response.details).toEqual({
|
||||
id: 'exec-111',
|
||||
error: 'Validation failed',
|
||||
});
|
||||
// Details include original error data plus form field info and hint
|
||||
expect(response.details).toEqual(
|
||||
expect.objectContaining({
|
||||
id: 'exec-111',
|
||||
error: 'Validation failed',
|
||||
formFields: expect.any(Array),
|
||||
hint: expect.any(String),
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle error with code', async () => {
|
||||
@@ -535,14 +539,12 @@ describe('FormHandler', () => {
|
||||
const response = await handler.execute(input, workflow, triggerInfo);
|
||||
|
||||
expect(response.success).toBe(true);
|
||||
expect(axios.request).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
data: {},
|
||||
})
|
||||
);
|
||||
// Even empty formData is sent as FormData
|
||||
const config = vi.mocked(axios.request).mock.calls[0][0];
|
||||
expect(config.data).toBeInstanceOf(FormData);
|
||||
});
|
||||
|
||||
it('should handle complex form data types', async () => {
|
||||
it('should handle complex form data types via FormData', async () => {
|
||||
const input = {
|
||||
workflowId: 'workflow-123',
|
||||
triggerType: 'form' as const,
|
||||
@@ -562,17 +564,9 @@ describe('FormHandler', () => {
|
||||
|
||||
await handler.execute(input, workflow, triggerInfo);
|
||||
|
||||
expect(axios.request).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
data: {
|
||||
name: 'Test User',
|
||||
age: 30,
|
||||
active: true,
|
||||
tags: ['tag1', 'tag2'],
|
||||
metadata: { key: 'value' },
|
||||
},
|
||||
})
|
||||
);
|
||||
// Complex data types are serialized in FormData
|
||||
const config = vi.mocked(axios.request).mock.calls[0][0];
|
||||
expect(config.data).toBeInstanceOf(FormData);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -242,7 +242,7 @@ describe('Trigger Detector', () => {
|
||||
expect(url).toContain('/form/');
|
||||
});
|
||||
|
||||
it('should build chat URL correctly', () => {
|
||||
it('should build chat URL correctly with /chat suffix', () => {
|
||||
const baseUrl = 'https://n8n.example.com';
|
||||
const trigger = {
|
||||
type: 'chat' as const,
|
||||
@@ -259,7 +259,8 @@ describe('Trigger Detector', () => {
|
||||
|
||||
const url = buildTriggerUrl(baseUrl, trigger, 'production');
|
||||
|
||||
expect(url).toBe('https://n8n.example.com/webhook/ai-chat');
|
||||
// Chat triggers use /webhook/<webhookId>/chat endpoint
|
||||
expect(url).toBe('https://n8n.example.com/webhook/ai-chat/chat');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user