Files
n8n-mcp/docs/local/integration-testing-plan.md
czlonkowski dad3a442d9 refactor(integration): update Phase 2 tests to use MCP handlers
**Critical Fix**: Tests now properly test the MCP handler layer (the actual product) instead of raw API client.

**Changes**:
- All 15 tests now use `handleCreateWorkflow()` MCP handler
- Tests validate `McpToolResponse` structure (`success`, `data`, `error`)
- Created `mcp-context.ts` helper for configuring InstanceContext
- Fixed ERROR_HANDLING_WORKFLOW to add main connection (MCP validation requirement)
- Updated error/edge case tests to expect validation failures (correct MCP behavior)

**MCP Handler Validation**:
- Error scenarios now correctly expect `success: false` with validation errors
- Edge cases updated to reflect MCP handler's proper pre-validation
- Documents that MCP validation is CORRECT behavior (catches errors early)

**Test Results**: All 15 scenarios passing
- 8 valid workflow tests → expect `success: true`
- 7 validation tests (errors/edge cases) → expect `success: false`

**Why This Matters**:
AI assistants interact with MCP handlers, not raw API client. Testing the wrong layer would miss MCP-specific logic and validation.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-04 11:22:23 +02:00

1164 lines
37 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Comprehensive Integration Testing Plan
## Status
**Phase 1: Foundation****COMPLETE** (October 3, 2025)
- All utility files created and tested
- Webhook workflows created on `https://n8n-test.n8n-mcp.com`
- GitHub secrets configured
- Critical fix: Updated credentials to use webhook URLs instead of IDs
- Environment loading fixed to support real n8n API integration tests
**Phase 2: Workflow Creation Tests****COMPLETE** (October 3, 2025)
- 15 test scenarios implemented and passing
- P0 bug verification confirmed (FULL node type format)
- All test categories covered: base nodes, advanced features, error scenarios, edge cases
- Documented actual n8n API behavior (validation at execution time, not creation time)
- Test file: `tests/integration/n8n-api/workflows/create-workflow.test.ts` (484 lines)
**Next Phase**: Phase 3 - Workflow Retrieval Tests
---
## Overview
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.
### What We Test: MCP Handlers (The Product Layer)
**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:**
- **MCP handlers** wrap API responses in `McpToolResponse` format: `{ success: boolean, data?: any, error?: string }`
- **MCP handlers** transform and enrich API responses (e.g., `handleGetWorkflowDetails` adds execution stats)
- **MCP handlers** provide the exact interface that AI assistants consume
- Testing raw API client bypasses the product layer and misses MCP-specific logic
**Test Pattern:**
```typescript
// ❌ 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;
```
## Critical Requirements
1. **Credentials**:
- Local development: Read from `.env` file
- CI/GitHub Actions: Use GitHub secrets (`N8N_URL`, `N8N_API_KEY`)
2. **Pre-activated Webhook Workflows**:
- n8n API doesn't support workflow activation via API
- Need pre-created, activated workflows for webhook testing
- Store webhook URLs (not workflow IDs) in `.env`:
- `N8N_TEST_WEBHOOK_GET_URL` - GET method webhook URL
- `N8N_TEST_WEBHOOK_POST_URL` - POST method webhook URL
- `N8N_TEST_WEBHOOK_PUT_URL` - PUT method webhook URL
- `N8N_TEST_WEBHOOK_DELETE_URL` - DELETE method webhook URL
- **Rationale**: Webhook URLs are what the `n8n_trigger_webhook_workflow` tool needs. Workflow IDs are only for workflow management tests (which create workflows dynamically during test execution).
3. **100% Coverage Goal**: Test EVERY tool, EVERY operation, EVERY parameter combination
---
## Complete Test Coverage Matrix
### Total Test Scenarios: ~150+
#### Workflow Management (10 MCP handlers)
**1. `handleCreateWorkflow`** - 15+ scenarios (MCP handler testing)
- Create workflow with base nodes (webhook, httpRequest, set)
- Create workflow with langchain nodes (agent, aiChain)
- Invalid node types (error handling)
- Complex multi-node workflows
- Complex connection patterns
- **P0 Bug Verification**: SHORT vs FULL node type handling
- Missing required parameters
- Duplicate node names
- Invalid connection references
- Settings variations
**2. `handleGetWorkflow`** - 3 scenarios
- Successful retrieval
- Not found (invalid ID)
- Malformed ID
**3. `handleGetWorkflowDetails`** - 4 scenarios
- Basic workflow
- Workflow with metadata
- Workflow with version history
- Workflow with execution stats
**4. `handleGetWorkflowStructure`** - 2 scenarios
- Simple workflow
- Complex workflow (verify no parameter data)
**5. `handleGetWorkflowMinimal`** - 2 scenarios
- Active workflow
- Inactive workflow
**6. `handleUpdateWorkflow`** - 8+ scenarios
- Full workflow replacement
- Update nodes
- Update connections
- Update settings
- Update tags
- Validation errors
- Concurrent update conflicts
- Large workflow updates
**7. `handleUpdatePartialWorkflow`** - 30+ scenarios (15 operations × 2 paths)
**Node Operations (12 scenarios):**
- `addNode`: Success, duplicate name, invalid type, missing position
- `removeNode`: By ID, by name, not found, with connection cleanup
- `updateNode`: By ID, by name, invalid updates, nested parameter updates
- `moveNode`: Valid position, boundary positions
- `enableNode`: Success, already enabled
- `disableNode`: Success, already disabled
**Connection Operations (10 scenarios):**
- `addConnection`: Default ports, custom ports, invalid nodes
- `removeConnection`: Success, not found, with ignoreErrors
- `updateConnection`: Change ports, change indices
- `cleanStaleConnections`: Dry run, actual cleanup
- `replaceConnections`: Full replacement, validation
**Metadata Operations (8 scenarios):**
- `updateSettings`: Timezone, execution order, error workflow
- `updateName`: Valid, duplicate, empty
- `addTag`: New tag, existing tag
- `removeTag`: Existing, non-existing
**8. `handleDeleteWorkflow`** - 3 scenarios
- Successful deletion
- Not found
- Verify cleanup (workflow actually deleted)
**9. `handleListWorkflows`** - 12+ scenarios
- No filters (all workflows)
- Filter by active status (true/false)
- Filter by tags (single, multiple)
- Filter by projectId (enterprise feature)
- Pagination: first page, next page, last page
- Pagination: cursor handling
- Exclude pinned data
- Limit variations (1, 50, 100)
- Empty results
- Sort order verification
**10. `handleValidateWorkflow`** - 16 scenarios (4 profiles × 4 validation types)
**Validation Profiles:**
- `strict`: All validations enabled, strictest rules
- `runtime`: Production-ready validation
- `ai-friendly`: Relaxed rules for AI-generated workflows
- `minimal`: Basic structure validation only
**Validation Types (for each profile):**
- All validations enabled (default)
- Nodes only (`validateNodes: true`, others false)
- Connections only (`validateConnections: true`, others false)
- Expressions only (`validateExpressions: true`, others false)
**11. `handleAutofixWorkflow`** - 20+ scenarios
**Fix Types (5):**
- `expression-format`: Fix `{{}}` syntax issues
- `typeversion-correction`: Fix outdated typeVersion
- `error-output-config`: Fix error output configuration
- `node-type-correction`: Fix incorrect node types
- `webhook-missing-path`: Add missing webhook paths
**Confidence Levels (3):**
- `high`: Only apply high-confidence fixes
- `medium`: Apply high + medium confidence fixes
- `low`: Apply all fixes
**Test Matrix:**
- Each fix type with preview mode (`applyFixes: false`)
- Each fix type with apply mode (`applyFixes: true`)
- Confidence threshold filtering
- `maxFixes` parameter limiting
- Multiple fix types in single workflow
- No fixes available scenario
---
#### Execution Management (4 handlers)
**12. `handleTriggerWebhookWorkflow`** - 16+ scenarios
**HTTP Methods (4):**
- GET: Query parameters, no data
- POST: JSON body, form data, headers
- PUT: Update data, custom headers
- DELETE: Query parameters, headers
**Scenarios per method:**
- Basic trigger (no data)
- With request data
- With custom headers
- Wait for response (true/false)
- Workflow not found
- Invalid webhook URL
**13. `handleGetExecution`** - 20+ scenarios
**Execution Modes (4):**
- `preview`: Structure & counts only (no data)
- `summary`: 2 samples per node (default)
- `filtered`: Custom limits and node filters
- `full`: Complete execution data
**Scenarios per mode:**
- Successful execution
- Failed execution
- Running execution
- With input data (`includeInputData: true`)
- Node filters (`nodeNames: ['Node1', 'Node2']`)
- Item limits (`itemsLimit: 0, 2, 5, -1`)
- Not found
**14. `handleListExecutions`** - 10+ scenarios
- No filters (all executions)
- Filter by workflowId
- Filter by status (success, error, waiting)
- Filter by projectId
- Pagination: first page, next page, last page
- Include execution data (`includeData: true/false`)
- Limit variations (1, 50, 100)
- Empty results
**15. `handleDeleteExecution`** - 3 scenarios
- Successful deletion
- Not found
- Verify cleanup
---
#### System/Utility (3 handlers)
**16. `handleHealthCheck`** - 2 scenarios
- API available
- Feature availability check
**17. `handleListAvailableTools`** - 1 scenario
- List all tools
**18. `handleDiagnostic`** - 3 scenarios
- Basic diagnostic
- Verbose mode (`verbose: true`)
- Configuration display
---
## Implementation Phases
### Phase 1: Foundation (Branch: `feat/integration-tests-foundation`)
#### 1.1 Environment Configuration
**Update `.env.example`:**
```bash
# ========================================
# 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 URL
- `N8N_TEST_WEBHOOK_POST_URL`: Pre-activated POST webhook URL
- `N8N_TEST_WEBHOOK_PUT_URL`: Pre-activated PUT webhook URL
- `N8N_TEST_WEBHOOK_DELETE_URL`: Pre-activated DELETE webhook URL
**Note**: 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`.
#### 1.2 Directory Structure
```
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
```
#### 1.3 Core Utilities
**mcp-context.ts** - MCP context configuration for handler testing:
```typescript
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:
```typescript
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):
```typescript
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:
```typescript
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:
```typescript
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:
```typescript
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:
```typescript
/**
* 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
`);
});
}
```
---
### Phase 2: Workflow Creation Tests (P0)
**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:**
```typescript
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):
1. Create workflow with base webhook node (verify P0 bug fix)
2. Create workflow with base HTTP request node
3. Create workflow with langchain agent node
4. Create complex multi-node workflow
5. Create workflow with complex connections
6. Create workflow with custom settings
7. Create workflow with n8n expressions
8. Create workflow with error handling
9. Error: Invalid node type (documents API behavior)
10. Error: Missing required parameters (documents API behavior)
11. Error: Duplicate node names (documents API behavior)
12. Error: Invalid connection references (documents API behavior)
13. Edge case: Minimal single node workflow
14. Edge case: Empty connections object
15. Edge case: Workflow without settings
---
### Phase 3: Workflow Retrieval Tests (P1)
**Branch**: `feat/integration-tests-phase-3`
**Test Approach**: Tests MCP handlers (`handleGetWorkflow`, `handleGetWorkflowDetails`, `handleGetWorkflowStructure`, `handleGetWorkflowMinimal`)
**MCP Handler Pattern:**
```typescript
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)
---
### Phase 4: Workflow Update Tests (P1)
**Branch**: `feat/integration-tests-workflow-updates`
**Files**:
- `update-workflow.test.ts` (8+ scenarios)
- `update-partial-workflow.test.ts` (30+ scenarios covering all 15 operations)
---
### Phase 5: Workflow Management Tests (P2)
**Branch**: `feat/integration-tests-workflow-management`
**Files**:
- `delete-workflow.test.ts` (3 scenarios)
- `list-workflows.test.ts` (12+ scenarios with all filters and pagination)
---
### Phase 6: Validation & Autofix Tests (P2)
**Branch**: `feat/integration-tests-validation`
**Files**:
- `validate-workflow.test.ts` (16 scenarios: 4 profiles × 4 validation types)
- `autofix-workflow.test.ts` (20+ scenarios: 5 fix types × confidence levels)
---
### Phase 7: Execution Management Tests (P2)
**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**:
- Use pre-activated workflows from `.env`
- Each HTTP method uses a different workflow ID
- Test both successful triggers and error cases
- Verify response data for synchronous executions
---
### Phase 8: System Tools Tests (P3)
**Branch**: `feat/integration-tests-system`
**Files**:
- `health-check.test.ts` (2 scenarios)
- `list-tools.test.ts` (1 scenario)
- `diagnostic.test.ts` (3 scenarios)
---
### Phase 9: CI/CD Integration
**Branch**: `feat/integration-tests-ci`
**GitHub Actions Workflow** (`.github/workflows/integration-tests.yml`):
```yaml
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`**:
```json
{
"scripts": {
"test:integration:n8n": "vitest run tests/integration/n8n-api",
"test:cleanup:orphans": "tsx tests/integration/n8n-api/utils/cleanup-orphans.ts"
}
}
```
---
## Test Isolation Strategy
### Workflow Naming Convention
- Prefix: `[MCP-TEST]`
- Include test name: `[MCP-TEST] Create Workflow - Base Nodes`
- Include timestamp for uniqueness: `[MCP-TEST] Test Name ${Date.now()}`
### Workflow Tagging
- All test workflows tagged with: `mcp-integration-test`
- Enables bulk cleanup queries
### Cleanup Levels
1. **Test-level**: After each test via `afterEach` hook
2. **Suite-level**: After each test file via `afterAll` hook
3. **CI-level**: After CI job completes (always run)
4. **Orphan cleanup**: Periodic job to clean up failed test runs
---
## Pre-Test Setup Checklist
### Local Development
1. ✅ Install n8n locally or use Docker
2. ✅ Start n8n instance: `npx n8n start`
3. ✅ Create 4 webhook workflows (GET, POST, PUT, DELETE)
4. ✅ Activate all 4 webhook workflows in n8n UI
5. ✅ Get webhook URLs from the workflow's Webhook node
6. ✅ Copy `.env.example` to `.env`
7. ✅ Set `N8N_API_URL=<your-n8n-url>`
8. ✅ Generate API key in n8n Settings > API
9. ✅ Set `N8N_API_KEY=<your-key>`
10. ✅ Set all 4 `N8N_TEST_WEBHOOK_*_URL` variables with full webhook URLs
### CI/GitHub Actions (✅ COMPLETED)
1. ✅ Set up cloud n8n instance: `https://n8n-test.n8n-mcp.com`
2. ✅ Create 4 webhook workflows (GET, POST, PUT, DELETE)
3. ✅ Activate all 4 webhook workflows
4. ✅ Add GitHub secrets: `N8N_URL`, `N8N_API_KEY`
5. ✅ Add webhook URL secrets:
- `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`
---
## Success Criteria
### Phase 1: Foundation ✅ COMPLETE
- ✅ Environment configuration (.env, GitHub secrets)
- ✅ All utility files created (8 files, ~1,520 lines of code)
- ✅ Pre-activated webhook workflows created and tested
- ✅ Cleanup helpers with pagination safety
- ✅ Resource tracking with TestContext
- ✅ Fixtures and factories for test data
- ✅ Documentation updated
- ✅ Environment loading fixed (loads .env before test defaults)
- ✅ Vitest integration config updated (removed MSW for n8n-api tests)
### Phase 2: Workflow Creation Tests ✅ COMPLETE
- ✅ 15 test scenarios implemented (all passing)
- ✅ Tests the `handleCreateWorkflow` MCP handler (product layer)
- ✅ All tests use MCP handler pattern with McpToolResponse validation
- ✅ P0 bug verification (FULL vs SHORT node type format)
- ✅ Base node tests (webhook, HTTP, langchain, multi-node)
- ✅ Advanced features (connections, settings, expressions, error handling)
- ✅ Error scenarios (4 tests documenting actual API behavior)
- ✅ Edge cases (3 tests for minimal/empty configurations)
- ✅ Test file: 563 lines covering all handleCreateWorkflow scenarios
- ✅ All tests passing against real n8n instance
### Overall Project (In Progress)
- ⏳ All 17 handlers have integration tests (1 of 17 complete)
- ⏳ All operations/parameters covered (15 of 150+ scenarios complete)
- ✅ Tests run successfully locally (Phase 2 verified)
- ⏳ Tests run successfully in CI (pending Phase 9)
- ✅ No manual cleanup required (automatic)
- ✅ Test coverage catches P0-level bugs (verified in Phase 2)
- ⏳ CI runs on every PR and daily (pending Phase 9)
- ✅ Clear error messages when tests fail
- ✅ Documentation for webhook workflow setup
---
## Timeline Estimate
- **Phase 1 (Foundation)**: ✅ COMPLETE (October 3, 2025)
- **Phase 2 (Workflow Creation)**: ✅ COMPLETE (October 3, 2025)
- **Phase 3 (Retrieval)**: 1 day
- **Phase 4 (Updates)**: 2-3 days (15 operations)
- **Phase 5 (Management)**: 1 day
- **Phase 6 (Validation)**: 2 days
- **Phase 7 (Executions)**: 2 days
- **Phase 8 (System)**: 1 day
- **Phase 9 (CI/CD)**: 1 day
**Total**: 2 days complete (~4-6 hours actual), ~12-16 days remaining
---
## Notes
- Each phase should be developed on a separate branch
- Phases can be parallelized where dependencies allow
- Run local tests frequently to catch issues early
- Document any n8n API quirks discovered during testing
## Key Learnings from Implementation
### Critical Testing Principle: Test the Product Layer
**The Mistake**: Initially, Phase 2 tests called `client.createWorkflow()` (raw API client) instead of `handleCreateWorkflow()` (MCP handler).
**Why This Was Wrong**:
- AI assistants interact with MCP handlers, not raw API client
- MCP handlers wrap responses in `McpToolResponse` format
- MCP handlers may transform/enrich API responses
- Bypassing MCP layer misses product-specific logic and bugs
**The Fix**: All tests updated to use MCP handlers:
```typescript
// ❌ 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 API Behavior Discoveries
1. **Validation Timing**: n8n API accepts workflows with invalid node types and connection references at creation time. Validation only happens at execution time.
2. **Node Type Format**: FULL node type format (`n8n-nodes-base.*`) must be used in API requests. The P0 bug was confirmed fixed.
3. **Missing Parameters**: n8n accepts workflows with missing required parameters. They fail during execution, not creation.
4. **Duplicate Names**: n8n API handles duplicate node names gracefully (may auto-rename).
### Technical Implementation Insights
1. **MSW Interference**: Integration tests that need real network requests must NOT load MSW setup. Removed from vitest.config.integration.ts.
2. **Environment Loading**: Must load `.env` file BEFORE test defaults in global setup to preserve real credentials.
3. **Cleanup Safety**: TestContext pattern works well for tracking and cleaning up test resources.
4. **Test Isolation**: Each test creates unique workflows with timestamps to avoid conflicts.