mirror of
https://github.com/czlonkowski/n8n-mcp.git
synced 2026-03-20 09:23:07 +00:00
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>
This commit is contained in:
924
docs/local/integration-testing-plan.md
Normal file
924
docs/local/integration-testing-plan.md
Normal file
@@ -0,0 +1,924 @@
|
||||
# Comprehensive Integration Testing Plan
|
||||
|
||||
## Overview
|
||||
|
||||
Transform the test suite to test all 17 n8n API 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.
|
||||
|
||||
## 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 workflow IDs in `.env`:
|
||||
- `N8N_TEST_WEBHOOK_GET_ID` - Webhook with GET method
|
||||
- `N8N_TEST_WEBHOOK_POST_ID` - Webhook with POST method
|
||||
- `N8N_TEST_WEBHOOK_PUT_ID` - Webhook with PUT method
|
||||
- `N8N_TEST_WEBHOOK_DELETE_ID` - Webhook with DELETE method
|
||||
|
||||
3. **100% Coverage Goal**: Test EVERY tool, EVERY operation, EVERY parameter combination
|
||||
|
||||
---
|
||||
|
||||
## Complete Test Coverage Matrix
|
||||
|
||||
### Total Test Scenarios: ~150+
|
||||
|
||||
#### Workflow Management (10 handlers)
|
||||
|
||||
**1. `handleCreateWorkflow`** - 10+ scenarios
|
||||
- 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 Workflows for Testing
|
||||
# Create these workflows manually in n8n and activate them
|
||||
# Each workflow should have a single Webhook node with the specified HTTP method
|
||||
N8N_TEST_WEBHOOK_GET_ID= # Webhook with GET method
|
||||
N8N_TEST_WEBHOOK_POST_ID= # Webhook with POST method
|
||||
N8N_TEST_WEBHOOK_PUT_ID= # Webhook with PUT method
|
||||
N8N_TEST_WEBHOOK_DELETE_ID= # Webhook with DELETE method
|
||||
|
||||
# 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
|
||||
- `N8N_API_KEY`: n8n API key
|
||||
- `N8N_TEST_WEBHOOK_GET_ID`: Pre-activated GET webhook workflow ID
|
||||
- `N8N_TEST_WEBHOOK_POST_ID`: Pre-activated POST webhook workflow ID
|
||||
- `N8N_TEST_WEBHOOK_PUT_ID`: Pre-activated PUT webhook workflow ID
|
||||
- `N8N_TEST_WEBHOOK_DELETE_ID`: Pre-activated DELETE webhook workflow ID
|
||||
|
||||
#### 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
|
||||
|
||||
**credentials.ts** - Environment-aware credential loader:
|
||||
```typescript
|
||||
import dotenv from 'dotenv';
|
||||
|
||||
dotenv.config();
|
||||
|
||||
export interface N8nTestCredentials {
|
||||
url: string;
|
||||
apiKey: string;
|
||||
webhookWorkflows: {
|
||||
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
|
||||
return {
|
||||
url: process.env.N8N_URL!,
|
||||
apiKey: process.env.N8N_API_KEY!,
|
||||
webhookWorkflows: {
|
||||
get: process.env.N8N_TEST_WEBHOOK_GET_ID!,
|
||||
post: process.env.N8N_TEST_WEBHOOK_POST_ID!,
|
||||
put: process.env.N8N_TEST_WEBHOOK_PUT_ID!,
|
||||
delete: process.env.N8N_TEST_WEBHOOK_DELETE_ID!
|
||||
},
|
||||
cleanup: {
|
||||
enabled: true,
|
||||
tag: 'mcp-integration-test',
|
||||
namePrefix: '[MCP-TEST]'
|
||||
}
|
||||
};
|
||||
} else {
|
||||
// Local: Use .env file
|
||||
return {
|
||||
url: process.env.N8N_API_URL!,
|
||||
apiKey: process.env.N8N_API_KEY!,
|
||||
webhookWorkflows: {
|
||||
get: process.env.N8N_TEST_WEBHOOK_GET_ID || '',
|
||||
post: process.env.N8N_TEST_WEBHOOK_POST_ID || '',
|
||||
put: process.env.N8N_TEST_WEBHOOK_PUT_ID || '',
|
||||
delete: process.env.N8N_TEST_WEBHOOK_DELETE_ID || ''
|
||||
},
|
||||
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 validateWebhookWorkflows(creds: N8nTestCredentials): void {
|
||||
const missing: string[] = [];
|
||||
if (!creds.webhookWorkflows.get) missing.push('GET');
|
||||
if (!creds.webhookWorkflows.post) missing.push('POST');
|
||||
if (!creds.webhookWorkflows.put) missing.push('PUT');
|
||||
if (!creds.webhookWorkflows.delete) missing.push('DELETE');
|
||||
|
||||
if (missing.length > 0) {
|
||||
throw new Error(
|
||||
`Missing webhook workflow IDs for HTTP methods: ${missing.join(', ')}\n` +
|
||||
`Please create and activate webhook workflows, then set:\n` +
|
||||
missing.map(m => ` N8N_TEST_WEBHOOK_${m}_ID`).join('\n')
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**n8n-client.ts** - Pre-configured API client wrapper:
|
||||
```typescript
|
||||
import { N8nApiClient } from '../../../src/services/n8n-api-client';
|
||||
import { getN8nCredentials } from './credentials';
|
||||
|
||||
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-workflow-creation`
|
||||
|
||||
**File**: `tests/integration/n8n-api/workflows/create-workflow.test.ts`
|
||||
|
||||
**10+ Test Scenarios**:
|
||||
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. Error: Invalid node type
|
||||
7. Error: Missing required parameters
|
||||
8. Error: Duplicate node names
|
||||
9. Error: Invalid connection references
|
||||
10. Create workflow with custom settings
|
||||
|
||||
---
|
||||
|
||||
### Phase 3: Workflow Retrieval Tests (P1)
|
||||
|
||||
**Branch**: `feat/integration-tests-workflow-retrieval`
|
||||
|
||||
**Files**:
|
||||
- `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 (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_ID: ${{ secrets.N8N_TEST_WEBHOOK_GET_ID }}
|
||||
N8N_TEST_WEBHOOK_POST_ID: ${{ secrets.N8N_TEST_WEBHOOK_POST_ID }}
|
||||
N8N_TEST_WEBHOOK_PUT_ID: ${{ secrets.N8N_TEST_WEBHOOK_PUT_ID }}
|
||||
N8N_TEST_WEBHOOK_DELETE_ID: ${{ secrets.N8N_TEST_WEBHOOK_DELETE_ID }}
|
||||
CI: true
|
||||
run: npm run test:integration
|
||||
|
||||
- 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 workflow IDs from n8n UI
|
||||
6. ✅ Copy `.env.example` to `.env`
|
||||
7. ✅ Set `N8N_API_URL=http://localhost:5678`
|
||||
8. ✅ Generate API key in n8n Settings > API
|
||||
9. ✅ Set `N8N_API_KEY=<your-key>`
|
||||
10. ✅ Set all 4 `N8N_TEST_WEBHOOK_*_ID` variables
|
||||
|
||||
### CI/GitHub Actions
|
||||
1. ✅ Set up cloud n8n instance (or self-hosted)
|
||||
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 workflow ID secrets (4 total)
|
||||
|
||||
---
|
||||
|
||||
## Success Criteria
|
||||
|
||||
- ✅ All 17 handlers have integration tests
|
||||
- ✅ All operations/parameters covered (150+ scenarios)
|
||||
- ✅ Tests run successfully locally and in CI
|
||||
- ✅ No manual cleanup required (automatic)
|
||||
- ✅ Test coverage catches P0-level bugs
|
||||
- ✅ CI runs on every PR and daily
|
||||
- ✅ Clear error messages when tests fail
|
||||
- ✅ Documentation for webhook workflow setup
|
||||
|
||||
---
|
||||
|
||||
## Timeline Estimate
|
||||
|
||||
- **Phase 1 (Foundation)**: 2-3 days
|
||||
- **Phase 2 (Workflow Creation)**: 1 day
|
||||
- **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**: ~14-18 days
|
||||
|
||||
---
|
||||
|
||||
## 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
|
||||
Reference in New Issue
Block a user