docs/local/integration-testing-plan.md
Phase 1: Foundation ✅ COMPLETE (October 3, 2025)
https://n8n-test.n8n-mcp.comPhase 2: Workflow Creation Tests ✅ COMPLETE (October 3, 2025)
tests/integration/n8n-api/workflows/create-workflow.test.ts (484 lines)Phase 3: Workflow Retrieval Tests ✅ COMPLETE (October 3, 2025)
get-workflow.test.ts (3 scenarios)get-workflow-details.test.ts (4 scenarios)get-workflow-structure.test.ts (2 scenarios)get-workflow-minimal.test.ts (2 scenarios)Phase 4: Workflow Update Tests ✅ COMPLETE (October 4, 2025)
Phase 5: Workflow Management Tests ✅ COMPLETE (October 4, 2025)
Phase 6A: Workflow Validation Tests ✅ COMPLETE (October 5, 2025)
Phase 6B: Workflow Autofix Tests ✅ COMPLETE (October 5, 2025)
as any casts with proper type definitionsPhase 7: Execution Management Tests ✅ COMPLETE (October 5, 2025)
Phase 8: System Tools Tests ✅ COMPLETE (October 5, 2025)
🎉 INTEGRATION TEST SUITE COMPLETE: All 18 MCP handlers fully tested
Next Phase: Update documentation and finalize integration testing plan
Transform the test suite to test all 17 MCP handlers against a real n8n instance instead of mocks. This plan ensures 100% coverage of every tool, operation, and parameter combination to prevent bugs like the P0 workflow creation issue from slipping through.
IMPORTANT: These integration tests validate the MCP handler layer (the actual product that AI assistants interact with), not just the raw n8n API client.
Architecture:
AI Assistant (Claude)
↓
MCP Tools (What AI sees)
↓
MCP Handlers (What we test) ← INTEGRATION TESTS TARGET THIS LAYER
↓
N8nApiClient (Low-level HTTP)
↓
n8n REST API
Why This Matters:
McpToolResponse format: { success: boolean, data?: any, error?: string }handleGetWorkflowDetails adds execution stats)Test Pattern:
// ❌ WRONG: Testing raw API client (low-level service)
const result = await client.createWorkflow(workflow);
// ✅ CORRECT: Testing MCP handler (product layer)
const response = await handleCreateWorkflow({ ...workflow }, mcpContext);
expect(response.success).toBe(true);
const result = response.data;
Credentials:
.env fileN8N_URL, N8N_API_KEY)Pre-activated Webhook Workflows:
.env:
N8N_TEST_WEBHOOK_GET_URL - GET method webhook URLN8N_TEST_WEBHOOK_POST_URL - POST method webhook URLN8N_TEST_WEBHOOK_PUT_URL - PUT method webhook URLN8N_TEST_WEBHOOK_DELETE_URL - DELETE method webhook URLn8n_trigger_webhook_workflow tool needs. Workflow IDs are only for workflow management tests (which create workflows dynamically during test execution).100% Coverage Goal: Test EVERY tool, EVERY operation, EVERY parameter combination
1. handleCreateWorkflow - 15+ scenarios (MCP handler testing)
2. handleGetWorkflow - 3 scenarios
3. handleGetWorkflowDetails - 4 scenarios
4. handleGetWorkflowStructure - 2 scenarios
5. handleGetWorkflowMinimal - 2 scenarios
6. handleUpdateWorkflow - 8+ scenarios
7. handleUpdatePartialWorkflow - 30+ scenarios (15 operations × 2 paths)
Node Operations (12 scenarios):
addNode: Success, duplicate name, invalid type, missing positionremoveNode: By ID, by name, not found, with connection cleanupupdateNode: By ID, by name, invalid updates, nested parameter updatesmoveNode: Valid position, boundary positionsenableNode: Success, already enableddisableNode: Success, already disabledConnection Operations (10 scenarios):
addConnection: Default ports, custom ports, invalid nodesremoveConnection: Success, not found, with ignoreErrorsupdateConnection: Change ports, change indicescleanStaleConnections: Dry run, actual cleanupreplaceConnections: Full replacement, validationMetadata Operations (8 scenarios):
updateSettings: Timezone, execution order, error workflowupdateName: Valid, duplicate, emptyaddTag: New tag, existing tagremoveTag: Existing, non-existing8. handleDeleteWorkflow - 3 scenarios
9. handleListWorkflows - 12+ scenarios
10. handleValidateWorkflow - 16 scenarios (4 profiles × 4 validation types)
Validation Profiles:
strict: All validations enabled, strictest rulesruntime: Production-ready validationai-friendly: Relaxed rules for AI-generated workflowsminimal: Basic structure validation onlyValidation Types (for each profile):
validateNodes: true, others false)validateConnections: true, others false)validateExpressions: true, others false)11. handleAutofixWorkflow - 20+ scenarios
Fix Types (5):
expression-format: Fix {{}} syntax issuestypeversion-correction: Fix outdated typeVersionerror-output-config: Fix error output configurationnode-type-correction: Fix incorrect node typeswebhook-missing-path: Add missing webhook pathsConfidence Levels (3):
high: Only apply high-confidence fixesmedium: Apply high + medium confidence fixeslow: Apply all fixesTest Matrix:
applyFixes: false)applyFixes: true)maxFixes parameter limiting12. handleTriggerWebhookWorkflow - 16+ scenarios
HTTP Methods (4):
Scenarios per method:
13. handleGetExecution - 20+ scenarios
Execution Modes (4):
preview: Structure & counts only (no data)summary: 2 samples per node (default)filtered: Custom limits and node filtersfull: Complete execution dataScenarios per mode:
includeInputData: true)nodeNames: ['Node1', 'Node2'])itemsLimit: 0, 2, 5, -1)14. handleListExecutions - 10+ scenarios
includeData: true/false)15. handleDeleteExecution - 3 scenarios
16. handleHealthCheck - 2 scenarios
17. handleListAvailableTools - 1 scenario
18. handleDiagnostic - 3 scenarios
verbose: true)feat/integration-tests-foundation)Update .env.example:
# ========================================
# INTEGRATION TESTING CONFIGURATION
# ========================================
# n8n API Configuration for Integration Tests
N8N_API_URL=http://localhost:5678
N8N_API_KEY=your-api-key-here
# Pre-activated Webhook URLs for Testing
# Create these workflows manually in n8n and activate them
# Store the full webhook URLs (not workflow IDs)
N8N_TEST_WEBHOOK_GET_URL=https://n8n-test.n8n-mcp.com/webhook/mcp-test-get
N8N_TEST_WEBHOOK_POST_URL=https://n8n-test.n8n-mcp.com/webhook/mcp-test-post
N8N_TEST_WEBHOOK_PUT_URL=https://n8n-test.n8n-mcp.com/webhook/mcp-test-put
N8N_TEST_WEBHOOK_DELETE_URL=https://n8n-test.n8n-mcp.com/webhook/mcp-test-delete
# Test Configuration
N8N_TEST_CLEANUP_ENABLED=true # Enable automatic cleanup
N8N_TEST_TAG=mcp-integration-test # Tag for test workflows
N8N_TEST_NAME_PREFIX=[MCP-TEST] # Name prefix for test workflows
GitHub Secrets (for CI):
N8N_URL: n8n instance URL (e.g., https://n8n-test.n8n-mcp.com)N8N_API_KEY: n8n API key (JWT token from n8n Settings > API)N8N_TEST_WEBHOOK_GET_URL: Pre-activated GET webhook URLN8N_TEST_WEBHOOK_POST_URL: Pre-activated POST webhook URLN8N_TEST_WEBHOOK_PUT_URL: Pre-activated PUT webhook URLN8N_TEST_WEBHOOK_DELETE_URL: Pre-activated DELETE webhook URLNote: Webhook URLs can be stored as repository secrets (not environment secrets) since they don't grant API access. The real secret is N8N_API_KEY.
tests/integration/n8n-api/
├── workflows/
│ ├── create-workflow.test.ts (10+ scenarios)
│ ├── get-workflow.test.ts (3 scenarios)
│ ├── get-workflow-details.test.ts (4 scenarios)
│ ├── get-workflow-structure.test.ts (2 scenarios)
│ ├── get-workflow-minimal.test.ts (2 scenarios)
│ ├── update-workflow.test.ts (8+ scenarios)
│ ├── update-partial-workflow.test.ts (30+ scenarios - 15 operations)
│ ├── delete-workflow.test.ts (3 scenarios)
│ ├── list-workflows.test.ts (12+ scenarios)
│ ├── validate-workflow.test.ts (16 scenarios - 4 profiles × 4 types)
│ └── autofix-workflow.test.ts (20+ scenarios - 5 types × modes)
├── executions/
│ ├── trigger-webhook.test.ts (16+ scenarios - 4 methods)
│ ├── get-execution.test.ts (20+ scenarios - 4 modes)
│ ├── list-executions.test.ts (10+ scenarios)
│ └── delete-execution.test.ts (3 scenarios)
├── system/
│ ├── health-check.test.ts (2 scenarios)
│ ├── list-tools.test.ts (1 scenario)
│ └── diagnostic.test.ts (3 scenarios)
└── utils/
├── credentials.ts # Environment-aware credential loader
├── n8n-client.ts # Pre-configured API client
├── cleanup-helpers.ts # Multi-level cleanup
├── test-context.ts # Resource tracking
├── fixtures.ts # Reusable workflow templates
├── factories.ts # Test data generators
└── webhook-workflows.ts # Webhook workflow configurations
mcp-context.ts - MCP context configuration for handler testing:
import { InstanceContext } from '../../../../src/types/instance-context';
import { getN8nCredentials } from './credentials';
/**
* Creates MCP context for testing MCP handlers against real n8n instance
* This is what gets passed to MCP handlers (handleCreateWorkflow, etc.)
*/
export function createMcpContext(): InstanceContext {
const creds = getN8nCredentials();
return {
n8nApiUrl: creds.url,
n8nApiKey: creds.apiKey
};
}
credentials.ts - Environment-aware credential loader:
import dotenv from 'dotenv';
dotenv.config();
export interface N8nTestCredentials {
url: string;
apiKey: string;
webhookUrls: {
get: string;
post: string;
put: string;
delete: string;
};
cleanup: {
enabled: boolean;
tag: string;
namePrefix: string;
};
}
export function getN8nCredentials(): N8nTestCredentials {
if (process.env.CI) {
// CI: Use GitHub secrets
const url = process.env.N8N_URL;
const apiKey = process.env.N8N_API_KEY;
if (!url || !apiKey) {
throw new Error(
'Missing required CI credentials:\n' +
` N8N_URL: ${url ? 'set' : 'MISSING'}\n` +
` N8N_API_KEY: ${apiKey ? 'set' : 'MISSING'}\n` +
'Please configure GitHub secrets for integration tests.'
);
}
return {
url,
apiKey,
webhookUrls: {
get: process.env.N8N_TEST_WEBHOOK_GET_URL || '',
post: process.env.N8N_TEST_WEBHOOK_POST_URL || '',
put: process.env.N8N_TEST_WEBHOOK_PUT_URL || '',
delete: process.env.N8N_TEST_WEBHOOK_DELETE_URL || ''
},
cleanup: {
enabled: true,
tag: 'mcp-integration-test',
namePrefix: '[MCP-TEST]'
}
};
} else {
// Local: Use .env file
const url = process.env.N8N_API_URL;
const apiKey = process.env.N8N_API_KEY;
if (!url || !apiKey) {
throw new Error(
'Missing required credentials in .env:\n' +
` N8N_API_URL: ${url ? 'set' : 'MISSING'}\n` +
` N8N_API_KEY: ${apiKey ? 'set' : 'MISSING'}\n\n` +
'Please add these to your .env file.\n' +
'See .env.example for configuration details.'
);
}
return {
url,
apiKey,
webhookUrls: {
get: process.env.N8N_TEST_WEBHOOK_GET_URL || '',
post: process.env.N8N_TEST_WEBHOOK_POST_URL || '',
put: process.env.N8N_TEST_WEBHOOK_PUT_URL || '',
delete: process.env.N8N_TEST_WEBHOOK_DELETE_URL || ''
},
cleanup: {
enabled: process.env.N8N_TEST_CLEANUP_ENABLED !== 'false',
tag: process.env.N8N_TEST_TAG || 'mcp-integration-test',
namePrefix: process.env.N8N_TEST_NAME_PREFIX || '[MCP-TEST]'
}
};
}
}
export function validateCredentials(creds: N8nTestCredentials): void {
if (!creds.url) throw new Error('N8N_API_URL is required');
if (!creds.apiKey) throw new Error('N8N_API_KEY is required');
}
export function validateWebhookUrls(creds: N8nTestCredentials): void {
const missing: string[] = [];
if (!creds.webhookUrls.get) missing.push('GET');
if (!creds.webhookUrls.post) missing.push('POST');
if (!creds.webhookUrls.put) missing.push('PUT');
if (!creds.webhookUrls.delete) missing.push('DELETE');
if (missing.length > 0) {
throw new Error(
`Missing webhook URLs for HTTP methods: ${missing.join(', ')}\n` +
`Please create and activate webhook workflows, then set:\n` +
missing.map(m => ` N8N_TEST_WEBHOOK_${m}_URL`).join('\n')
);
}
}
n8n-client.ts - Pre-configured API client (for test utilities only):
import { N8nApiClient } from '../../../src/services/n8n-api-client';
import { getN8nCredentials } from './credentials';
/**
* IMPORTANT: This client is ONLY used for test setup/cleanup utilities.
* DO NOT use this in actual test cases - use MCP handlers instead!
*
* Test utilities that need direct API access:
* - cleanupOrphanedWorkflows() - bulk cleanup
* - Fixture setup/teardown
* - Pre-test verification
*
* Actual tests MUST use MCP handlers:
* - handleCreateWorkflow()
* - handleGetWorkflow()
* - etc.
*/
let client: N8nApiClient | null = null;
export function getTestN8nClient(): N8nApiClient {
if (!client) {
const creds = getN8nCredentials();
client = new N8nApiClient(creds.url, creds.apiKey);
}
return client;
}
export function resetTestN8nClient(): void {
client = null;
}
test-context.ts - Resource tracking for cleanup:
import { getN8nCredentials } from './credentials';
export interface TestContext {
workflowIds: string[];
executionIds: string[];
cleanup: () => Promise<void>;
}
export function createTestContext(): TestContext {
const context: TestContext = {
workflowIds: [],
executionIds: [],
cleanup: async () => {
const creds = getN8nCredentials();
if (!creds.cleanup.enabled) return;
const client = getTestN8nClient();
// Delete executions first
for (const id of context.executionIds) {
try {
await client.deleteExecution(id);
} catch (error) {
console.warn(`Failed to delete execution ${id}:`, error);
}
}
// Then delete workflows
for (const id of context.workflowIds) {
try {
await client.deleteWorkflow(id);
} catch (error) {
console.warn(`Failed to delete workflow ${id}:`, error);
}
}
context.workflowIds = [];
context.executionIds = [];
}
};
return context;
}
cleanup-helpers.ts - Multi-level cleanup strategies:
import { N8nApiClient } from '../../../src/services/n8n-api-client';
import { getN8nCredentials, getTestN8nClient } from './credentials';
/**
* Clean up orphaned test workflows
* Run this periodically in CI to clean up failed test runs
*/
export async function cleanupOrphanedWorkflows(): Promise<void> {
const creds = getN8nCredentials();
const client = getTestN8nClient();
let allWorkflows: any[] = [];
let cursor: string | undefined;
// Fetch all workflows with pagination
do {
const response = await client.listWorkflows({ cursor, limit: 100 });
allWorkflows.push(...response.data);
cursor = response.nextCursor;
} while (cursor);
// Find test workflows
const testWorkflows = allWorkflows.filter(w =>
w.tags?.includes(creds.cleanup.tag) ||
w.name?.startsWith(creds.cleanup.namePrefix)
);
console.log(`Found ${testWorkflows.length} orphaned test workflows`);
// Delete them
for (const workflow of testWorkflows) {
try {
await client.deleteWorkflow(workflow.id);
console.log(`Deleted orphaned workflow: ${workflow.name} (${workflow.id})`);
} catch (error) {
console.warn(`Failed to delete workflow ${workflow.id}:`, error);
}
}
}
/**
* Clean up old executions (older than 24 hours)
*/
export async function cleanupOldExecutions(): Promise<void> {
const client = getTestN8nClient();
let allExecutions: any[] = [];
let cursor: string | undefined;
// Fetch all executions
do {
const response = await client.listExecutions({ cursor, limit: 100 });
allExecutions.push(...response.data);
cursor = response.nextCursor;
} while (cursor);
const oneDayAgo = Date.now() - 24 * 60 * 60 * 1000;
const oldExecutions = allExecutions.filter(e =>
new Date(e.startedAt).getTime() < oneDayAgo
);
console.log(`Found ${oldExecutions.length} old executions`);
for (const execution of oldExecutions) {
try {
await client.deleteExecution(execution.id);
} catch (error) {
console.warn(`Failed to delete execution ${execution.id}:`, error);
}
}
}
fixtures.ts - Reusable workflow templates:
import { Workflow } from '../../../src/types/n8n-api';
export const SIMPLE_WEBHOOK_WORKFLOW: Partial<Workflow> = {
name: '[MCP-TEST] Simple Webhook',
nodes: [
{
id: 'webhook-1',
name: 'Webhook',
type: 'n8n-nodes-base.webhook',
typeVersion: 2,
position: [250, 300],
parameters: {
httpMethod: 'GET',
path: 'test-webhook'
}
}
],
connections: {}
};
export const SIMPLE_HTTP_WORKFLOW: Partial<Workflow> = {
name: '[MCP-TEST] Simple HTTP Request',
nodes: [
{
id: 'webhook-1',
name: 'Webhook',
type: 'n8n-nodes-base.webhook',
typeVersion: 2,
position: [250, 300],
parameters: {
httpMethod: 'GET',
path: 'trigger'
}
},
{
id: 'http-1',
name: 'HTTP Request',
type: 'n8n-nodes-base.httpRequest',
typeVersion: 4.2,
position: [450, 300],
parameters: {
url: 'https://httpbin.org/get',
method: 'GET'
}
}
],
connections: {
Webhook: {
main: [[{ node: 'HTTP Request', type: 'main', index: 0 }]]
}
}
};
// Add more fixtures for complex workflows
webhook-workflows.ts - Webhook workflow setup guide:
/**
* Guide for setting up webhook workflows manually in n8n
*
* These workflows must be created manually and activated because
* n8n API doesn't support workflow activation.
*
* For each HTTP method, create a workflow with:
* 1. Single Webhook node
* 2. Configured for the specific HTTP method
* 3. Unique webhook path
* 4. Activated in n8n UI
* 5. Workflow ID added to .env
*/
export const WEBHOOK_WORKFLOW_CONFIGS = {
GET: {
name: '[MCP-TEST] Webhook GET',
description: 'Pre-activated webhook for GET method testing',
nodes: [
{
name: 'Webhook',
type: 'n8n-nodes-base.webhook',
typeVersion: 2,
parameters: {
httpMethod: 'GET',
path: 'mcp-test-get',
responseMode: 'lastNode'
}
}
]
},
POST: {
name: '[MCP-TEST] Webhook POST',
description: 'Pre-activated webhook for POST method testing',
nodes: [
{
name: 'Webhook',
type: 'n8n-nodes-base.webhook',
typeVersion: 2,
parameters: {
httpMethod: 'POST',
path: 'mcp-test-post',
responseMode: 'lastNode'
}
}
]
},
PUT: {
name: '[MCP-TEST] Webhook PUT',
description: 'Pre-activated webhook for PUT method testing',
nodes: [
{
name: 'Webhook',
type: 'n8n-nodes-base.webhook',
typeVersion: 2,
parameters: {
httpMethod: 'PUT',
path: 'mcp-test-put',
responseMode: 'lastNode'
}
}
]
},
DELETE: {
name: '[MCP-TEST] Webhook DELETE',
description: 'Pre-activated webhook for DELETE method testing',
nodes: [
{
name: 'Webhook',
type: 'n8n-nodes-base.webhook',
typeVersion: 2,
parameters: {
httpMethod: 'DELETE',
path: 'mcp-test-delete',
responseMode: 'lastNode'
}
}
]
}
};
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.nodes[0].parameters.path}
.env variable: N8N_TEST_WEBHOOK_${method}_ID
`);
});
}
Branch: feat/integration-tests-phase-2
File: tests/integration/n8n-api/workflows/create-workflow.test.ts
Test Approach: Tests the handleCreateWorkflow MCP handler against real n8n instance
MCP Handler Test Pattern:
import { handleCreateWorkflow } from '../../../../src/mcp/handlers-n8n-manager';
import { createMcpContext } from '../utils/mcp-context';
import { InstanceContext } from '../../../../src/types/instance-context';
describe('Integration: handleCreateWorkflow', () => {
let mcpContext: InstanceContext;
beforeEach(() => {
mcpContext = createMcpContext();
});
it('should create workflow using MCP handler', async () => {
const workflow = { name: 'Test', nodes: [...], connections: {} };
// Test MCP handler (the product layer)
const response = await handleCreateWorkflow({ ...workflow }, mcpContext);
// Verify MCP response structure
expect(response.success).toBe(true);
expect(response.data).toBeDefined();
// Extract actual workflow from MCP response
const result = response.data;
expect(result.id).toBeTruthy();
});
});
15 Test Scenarios (all testing MCP handlers):
Branch: feat/integration-tests-phase-3
Test Approach: Tests MCP handlers (handleGetWorkflow, handleGetWorkflowDetails, handleGetWorkflowStructure, handleGetWorkflowMinimal)
MCP Handler Pattern:
import {
handleGetWorkflow,
handleGetWorkflowDetails,
handleGetWorkflowStructure,
handleGetWorkflowMinimal
} from '../../../../src/mcp/handlers-n8n-manager';
// Test MCP handler
const response = await handleGetWorkflow({ id: workflowId }, mcpContext);
expect(response.success).toBe(true);
const workflow = response.data;
// Note: handleGetWorkflowDetails returns nested structure
const detailsResponse = await handleGetWorkflowDetails({ id }, mcpContext);
const workflow = detailsResponse.data.workflow; // Extract from nested structure
const stats = detailsResponse.data.executionStats;
Files:
get-workflow.test.ts (3 scenarios - tests handleGetWorkflow)get-workflow-details.test.ts (4 scenarios - tests handleGetWorkflowDetails)get-workflow-structure.test.ts (2 scenarios - tests handleGetWorkflowStructure)get-workflow-minimal.test.ts (2 scenarios - tests handleGetWorkflowMinimal)Branch: feat/integration-tests-workflow-updates
Files:
update-workflow.test.ts (8+ scenarios)update-partial-workflow.test.ts (30+ scenarios covering all 15 operations)Branch: feat/integration-tests-workflow-management
Files:
delete-workflow.test.ts (3 scenarios)list-workflows.test.ts (12+ scenarios with all filters and pagination)Branch: feat/integration-tests-phase-6
Files:
tests/integration/n8n-api/utils/node-repository.ts - NodeRepository singleton for validation testsvalidate-workflow.test.ts (12 scenarios: 4 profiles + invalid detection + selective validation + error handling)Implementation Notes:
Branch: feat/integration-tests-phase-6b (or continue on feat/integration-tests-phase-6)
Files:
autofix-workflow.test.ts (15-20 scenarios: 5 fix types × modes × confidence levels)Test Coverage Required:
Branch: feat/integration-tests-executions
Files:
trigger-webhook.test.ts (16+ scenarios: 4 HTTP methods × variations)get-execution.test.ts (20+ scenarios: 4 modes × filters)list-executions.test.ts (10+ scenarios)delete-execution.test.ts (3 scenarios)Special Considerations for Webhook Testing:
.envBranch: feat/integration-tests-system
Files:
health-check.test.ts (2 scenarios)list-tools.test.ts (1 scenario)diagnostic.test.ts (3 scenarios)Branch: feat/integration-tests-ci
GitHub Actions Workflow (.github/workflows/integration-tests.yml):
name: Integration Tests
on:
pull_request:
push:
branches: [main]
schedule:
- cron: '0 2 * * *' # Daily at 2 AM
workflow_dispatch:
jobs:
integration-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build project
run: npm run build
- name: Run integration tests
env:
N8N_URL: ${{ secrets.N8N_URL }}
N8N_API_KEY: ${{ secrets.N8N_API_KEY }}
N8N_TEST_WEBHOOK_GET_URL: ${{ secrets.N8N_TEST_WEBHOOK_GET_URL }}
N8N_TEST_WEBHOOK_POST_URL: ${{ secrets.N8N_TEST_WEBHOOK_POST_URL }}
N8N_TEST_WEBHOOK_PUT_URL: ${{ secrets.N8N_TEST_WEBHOOK_PUT_URL }}
N8N_TEST_WEBHOOK_DELETE_URL: ${{ secrets.N8N_TEST_WEBHOOK_DELETE_URL }}
CI: true
run: npm run test:integration:n8n
- name: Cleanup orphaned workflows
if: always()
env:
N8N_URL: ${{ secrets.N8N_URL }}
N8N_API_KEY: ${{ secrets.N8N_API_KEY }}
run: npm run test:cleanup:orphans
Add npm scripts to package.json:
{
"scripts": {
"test:integration:n8n": "vitest run tests/integration/n8n-api",
"test:cleanup:orphans": "tsx tests/integration/n8n-api/utils/cleanup-orphans.ts"
}
}
[MCP-TEST][MCP-TEST] Create Workflow - Base Nodes[MCP-TEST] Test Name ${Date.now()}mcp-integration-testafterEach hookafterAll hooknpx n8n start.env.example to .envN8N_API_URL=<your-n8n-url>N8N_API_KEY=<your-key>N8N_TEST_WEBHOOK_*_URL variables with full webhook URLshttps://n8n-test.n8n-mcp.comN8N_URL, N8N_API_KEYN8N_TEST_WEBHOOK_GET_URL=https://n8n-test.n8n-mcp.com/webhook/mcp-test-getN8N_TEST_WEBHOOK_POST_URL=https://n8n-test.n8n-mcp.com/webhook/mcp-test-postN8N_TEST_WEBHOOK_PUT_URL=https://n8n-test.n8n-mcp.com/webhook/mcp-test-putN8N_TEST_WEBHOOK_DELETE_URL=https://n8n-test.n8n-mcp.com/webhook/mcp-test-deletehandleCreateWorkflow MCP handler (product layer)Total: 6 days complete, ~4 days remaining
The Mistake: Initially, Phase 2 tests called client.createWorkflow() (raw API client) instead of handleCreateWorkflow() (MCP handler).
Why This Was Wrong:
McpToolResponse formatThe Fix: All tests updated to use MCP handlers:
// ❌ BEFORE: Testing wrong layer
const result = await client.createWorkflow(workflow);
// ✅ AFTER: Testing the actual product
const response = await handleCreateWorkflow({ ...workflow }, mcpContext);
expect(response.success).toBe(true);
const result = response.data;
Lesson Learned: Always test the layer closest to the user/consumer. For n8n-mcp, that's the MCP handler layer.
n8n-nodes-base.*) must be used in API requests. The P0 bug was confirmed fixed..env file BEFORE test defaults in global setup to preserve real credentials.