Files
n8n-mcp/tests/integration/n8n-api/utils/webhook-workflows.ts
czlonkowski 2305aaab9e feat: implement integration testing foundation (Phase 1)
Complete implementation of Phase 1 foundation for n8n API integration tests.
Establishes core utilities, fixtures, and infrastructure for testing all 17 n8n API handlers against real n8n instance.

Changes:
- Add integration test environment configuration to .env.example
- Create comprehensive test utilities infrastructure:
  * credentials.ts: Environment-aware credential management (local .env vs CI secrets)
  * n8n-client.ts: Singleton API client wrapper with health checks
  * test-context.ts: Resource tracking and automatic cleanup
  * cleanup-helpers.ts: Multi-level cleanup strategies (orphaned, age-based, tag-based)
  * fixtures.ts: 6 pre-built workflow templates (webhook, HTTP, multi-node, error handling, AI, expressions)
  * factories.ts: Dynamic node/workflow builders with 15+ factory functions
  * webhook-workflows.ts: Webhook workflow configs and setup instructions

- Add npm scripts:
  * test:integration:n8n: Run n8n API integration tests
  * test:cleanup:orphans: Clean up orphaned test resources

- Create cleanup script for CI/manual use

Documentation:
- Add comprehensive integration testing plan (550 lines)
- Add Phase 1 completion summary with lessons learned

Key Features:
- Automatic credential detection (CI vs local)
- Multi-level cleanup (test, suite, CI, orphan)
- 6 workflow fixtures covering common scenarios
- 15+ factory functions for dynamic test data
- Support for 4 HTTP methods (GET, POST, PUT, DELETE) via pre-activated webhook workflows
- TypeScript-first with full type safety
- Comprehensive error handling with helpful messages

Total: ~1,520 lines of production-ready code + 650 lines of documentation

Ready for Phase 2: Workflow creation tests

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-03 13:12:42 +02:00

290 lines
7.9 KiB
TypeScript

/**
* Webhook Workflow Configuration
*
* Provides configuration and setup instructions for webhook workflows
* required for integration testing.
*
* These workflows must be created manually in n8n and activated because
* the n8n API doesn't support workflow activation.
*/
import { Workflow, WorkflowNode } from '../../../../src/types/n8n-api';
export interface WebhookWorkflowConfig {
name: string;
description: string;
httpMethod: 'GET' | 'POST' | 'PUT' | 'DELETE';
path: string;
nodes: Array<Partial<WorkflowNode>>;
connections: Record<string, any>;
}
/**
* Configuration for required webhook workflows
*/
export const WEBHOOK_WORKFLOW_CONFIGS: Record<string, WebhookWorkflowConfig> = {
GET: {
name: '[MCP-TEST] Webhook GET',
description: 'Pre-activated webhook for GET method testing',
httpMethod: 'GET',
path: 'mcp-test-get',
nodes: [
{
name: 'Webhook',
type: 'n8n-nodes-base.webhook',
typeVersion: 2,
position: [250, 300],
parameters: {
httpMethod: 'GET',
path: 'mcp-test-get',
responseMode: 'lastNode',
options: {}
}
},
{
name: 'Respond to Webhook',
type: 'n8n-nodes-base.respondToWebhook',
typeVersion: 1.1,
position: [450, 300],
parameters: {
options: {}
}
}
],
connections: {
Webhook: {
main: [[{ node: 'Respond to Webhook', type: 'main', index: 0 }]]
}
}
},
POST: {
name: '[MCP-TEST] Webhook POST',
description: 'Pre-activated webhook for POST method testing',
httpMethod: 'POST',
path: 'mcp-test-post',
nodes: [
{
name: 'Webhook',
type: 'n8n-nodes-base.webhook',
typeVersion: 2,
position: [250, 300],
parameters: {
httpMethod: 'POST',
path: 'mcp-test-post',
responseMode: 'lastNode',
options: {}
}
},
{
name: 'Respond to Webhook',
type: 'n8n-nodes-base.respondToWebhook',
typeVersion: 1.1,
position: [450, 300],
parameters: {
options: {}
}
}
],
connections: {
Webhook: {
main: [[{ node: 'Respond to Webhook', type: 'main', index: 0 }]]
}
}
},
PUT: {
name: '[MCP-TEST] Webhook PUT',
description: 'Pre-activated webhook for PUT method testing',
httpMethod: 'PUT',
path: 'mcp-test-put',
nodes: [
{
name: 'Webhook',
type: 'n8n-nodes-base.webhook',
typeVersion: 2,
position: [250, 300],
parameters: {
httpMethod: 'PUT',
path: 'mcp-test-put',
responseMode: 'lastNode',
options: {}
}
},
{
name: 'Respond to Webhook',
type: 'n8n-nodes-base.respondToWebhook',
typeVersion: 1.1,
position: [450, 300],
parameters: {
options: {}
}
}
],
connections: {
Webhook: {
main: [[{ node: 'Respond to Webhook', type: 'main', index: 0 }]]
}
}
},
DELETE: {
name: '[MCP-TEST] Webhook DELETE',
description: 'Pre-activated webhook for DELETE method testing',
httpMethod: 'DELETE',
path: 'mcp-test-delete',
nodes: [
{
name: 'Webhook',
type: 'n8n-nodes-base.webhook',
typeVersion: 2,
position: [250, 300],
parameters: {
httpMethod: 'DELETE',
path: 'mcp-test-delete',
responseMode: 'lastNode',
options: {}
}
},
{
name: 'Respond to Webhook',
type: 'n8n-nodes-base.respondToWebhook',
typeVersion: 1.1,
position: [450, 300],
parameters: {
options: {}
}
}
],
connections: {
Webhook: {
main: [[{ node: 'Respond to Webhook', type: 'main', index: 0 }]]
}
}
}
};
/**
* Print setup instructions for webhook workflows
*/
export function printSetupInstructions(): void {
console.log(`
╔════════════════════════════════════════════════════════════════╗
║ WEBHOOK WORKFLOW SETUP REQUIRED ║
╠════════════════════════════════════════════════════════════════╣
║ ║
║ Integration tests require 4 pre-activated webhook workflows: ║
║ ║
║ 1. Create workflows manually in n8n UI ║
║ 2. Use the configurations shown below ║
║ 3. ACTIVATE each workflow in n8n UI ║
║ 4. Copy workflow IDs to .env file ║
║ ║
╚════════════════════════════════════════════════════════════════╝
Required workflows:
`);
Object.entries(WEBHOOK_WORKFLOW_CONFIGS).forEach(([method, config]) => {
console.log(`
${method} Method:
Name: ${config.name}
Path: ${config.path}
.env variable: N8N_TEST_WEBHOOK_${method}_ID
Workflow Structure:
1. Webhook node (${method} method, path: ${config.path})
2. Respond to Webhook node
After creating:
1. Save the workflow
2. ACTIVATE the workflow (toggle in UI)
3. Copy the workflow ID
4. Add to .env: N8N_TEST_WEBHOOK_${method}_ID=<workflow-id>
`);
});
console.log(`
See docs/local/integration-testing-plan.md for detailed instructions.
`);
}
/**
* Generate workflow JSON for a webhook workflow
*
* @param method - HTTP method
* @returns Partial workflow ready to create
*/
export function generateWebhookWorkflowJson(
method: 'GET' | 'POST' | 'PUT' | 'DELETE'
): Partial<Workflow> {
const config = WEBHOOK_WORKFLOW_CONFIGS[method];
return {
name: config.name,
nodes: config.nodes as any,
connections: config.connections,
active: false, // Will need to be activated manually
settings: {
executionOrder: 'v1'
},
tags: ['mcp-integration-test', 'webhook-test']
};
}
/**
* Export all webhook workflow JSONs
*
* Returns an object with all 4 webhook workflow configurations
* ready to be created in n8n.
*
* @returns Object with workflow configurations
*/
export function exportAllWebhookWorkflows(): Record<string, Partial<Workflow>> {
return {
GET: generateWebhookWorkflowJson('GET'),
POST: generateWebhookWorkflowJson('POST'),
PUT: generateWebhookWorkflowJson('PUT'),
DELETE: generateWebhookWorkflowJson('DELETE')
};
}
/**
* Get webhook URL for a given n8n instance and HTTP method
*
* @param n8nUrl - n8n instance URL
* @param method - HTTP method
* @returns Webhook URL
*/
export function getWebhookUrl(
n8nUrl: string,
method: 'GET' | 'POST' | 'PUT' | 'DELETE'
): string {
const config = WEBHOOK_WORKFLOW_CONFIGS[method];
const baseUrl = n8nUrl.replace(/\/$/, ''); // Remove trailing slash
return `${baseUrl}/webhook/${config.path}`;
}
/**
* Validate webhook workflow structure
*
* Checks if a workflow matches the expected webhook workflow structure.
*
* @param workflow - Workflow to validate
* @param method - Expected HTTP method
* @returns true if valid
*/
export function isValidWebhookWorkflow(
workflow: Partial<Workflow>,
method: 'GET' | 'POST' | 'PUT' | 'DELETE'
): boolean {
if (!workflow.nodes || workflow.nodes.length < 1) {
return false;
}
const webhookNode = workflow.nodes.find(n => n.type === 'n8n-nodes-base.webhook');
if (!webhookNode) {
return false;
}
const params = webhookNode.parameters as any;
return params.httpMethod === method;
}