mirror of
https://github.com/czlonkowski/n8n-mcp.git
synced 2026-01-30 22:42:04 +00:00
Implement comprehensive integration tests for 4 execution management handlers: - handleTriggerWebhookWorkflow (20 tests): GET/POST/PUT/DELETE methods, headers, error handling - handleGetExecution (16 tests): 4 retrieval modes (preview/summary/filtered/full), filtering, legacy compatibility - handleListExecutions (13 tests): status filtering, pagination with cursor, data inclusion - handleDeleteExecution (5 tests): successful deletion with verification, error handling All 54 tests passing against real n8n instance. Coverage: - All HTTP methods (GET, POST, PUT, DELETE) - All execution retrieval modes with filtering options - Pagination with cursor handling - Execution creation and cleanup verification - Comprehensive error handling scenarios 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
264 lines
7.9 KiB
TypeScript
264 lines
7.9 KiB
TypeScript
/**
|
|
* Integration Tests: handleListExecutions
|
|
*
|
|
* Tests execution listing against a real n8n instance.
|
|
* Covers filtering, pagination, and various list parameters.
|
|
*/
|
|
|
|
import { describe, it, expect, beforeEach } from 'vitest';
|
|
import { createMcpContext } from '../utils/mcp-context';
|
|
import { InstanceContext } from '../../../../src/types/instance-context';
|
|
import { handleListExecutions } from '../../../../src/mcp/handlers-n8n-manager';
|
|
|
|
describe('Integration: handleListExecutions', () => {
|
|
let mcpContext: InstanceContext;
|
|
|
|
beforeEach(() => {
|
|
mcpContext = createMcpContext();
|
|
});
|
|
|
|
// ======================================================================
|
|
// No Filters
|
|
// ======================================================================
|
|
|
|
describe('No Filters', () => {
|
|
it('should list all executions without filters', async () => {
|
|
const response = await handleListExecutions({}, mcpContext);
|
|
|
|
expect(response.success).toBe(true);
|
|
expect(response.data).toBeDefined();
|
|
|
|
const data = response.data as any;
|
|
expect(Array.isArray(data.executions)).toBe(true);
|
|
expect(data).toHaveProperty('returned');
|
|
});
|
|
});
|
|
|
|
// ======================================================================
|
|
// Filter by Status
|
|
// ======================================================================
|
|
|
|
describe('Filter by Status', () => {
|
|
it('should filter executions by success status', async () => {
|
|
const response = await handleListExecutions(
|
|
{ status: 'success' },
|
|
mcpContext
|
|
);
|
|
|
|
expect(response.success).toBe(true);
|
|
const data = response.data as any;
|
|
|
|
expect(Array.isArray(data.executions)).toBe(true);
|
|
// All returned executions should have success status
|
|
if (data.executions.length > 0) {
|
|
data.executions.forEach((exec: any) => {
|
|
expect(exec.status).toBe('success');
|
|
});
|
|
}
|
|
});
|
|
|
|
it('should filter executions by error status', async () => {
|
|
const response = await handleListExecutions(
|
|
{ status: 'error' },
|
|
mcpContext
|
|
);
|
|
|
|
expect(response.success).toBe(true);
|
|
const data = response.data as any;
|
|
|
|
expect(Array.isArray(data.executions)).toBe(true);
|
|
// All returned executions should have error status
|
|
if (data.executions.length > 0) {
|
|
data.executions.forEach((exec: any) => {
|
|
expect(exec.status).toBe('error');
|
|
});
|
|
}
|
|
});
|
|
|
|
it('should filter executions by waiting status', async () => {
|
|
const response = await handleListExecutions(
|
|
{ status: 'waiting' },
|
|
mcpContext
|
|
);
|
|
|
|
expect(response.success).toBe(true);
|
|
const data = response.data as any;
|
|
|
|
expect(Array.isArray(data.executions)).toBe(true);
|
|
});
|
|
});
|
|
|
|
// ======================================================================
|
|
// Pagination
|
|
// ======================================================================
|
|
|
|
describe('Pagination', () => {
|
|
it('should return first page with limit', async () => {
|
|
const response = await handleListExecutions(
|
|
{ limit: 10 },
|
|
mcpContext
|
|
);
|
|
|
|
expect(response.success).toBe(true);
|
|
const data = response.data as any;
|
|
|
|
expect(Array.isArray(data.executions)).toBe(true);
|
|
expect(data.executions.length).toBeLessThanOrEqual(10);
|
|
});
|
|
|
|
it('should handle pagination with cursor', async () => {
|
|
// Get first page
|
|
const firstPage = await handleListExecutions(
|
|
{ limit: 5 },
|
|
mcpContext
|
|
);
|
|
|
|
expect(firstPage.success).toBe(true);
|
|
const firstData = firstPage.data as any;
|
|
|
|
// If there's a next cursor, get second page
|
|
if (firstData.nextCursor) {
|
|
const secondPage = await handleListExecutions(
|
|
{ limit: 5, cursor: firstData.nextCursor },
|
|
mcpContext
|
|
);
|
|
|
|
expect(secondPage.success).toBe(true);
|
|
const secondData = secondPage.data as any;
|
|
|
|
// Second page should have different executions
|
|
const firstIds = new Set(firstData.executions.map((e: any) => e.id));
|
|
const secondIds = secondData.executions.map((e: any) => e.id);
|
|
|
|
secondIds.forEach((id: string) => {
|
|
expect(firstIds.has(id)).toBe(false);
|
|
});
|
|
}
|
|
});
|
|
|
|
it('should respect limit=1', async () => {
|
|
const response = await handleListExecutions(
|
|
{ limit: 1 },
|
|
mcpContext
|
|
);
|
|
|
|
expect(response.success).toBe(true);
|
|
const data = response.data as any;
|
|
|
|
expect(data.executions.length).toBeLessThanOrEqual(1);
|
|
});
|
|
|
|
it('should respect limit=50', async () => {
|
|
const response = await handleListExecutions(
|
|
{ limit: 50 },
|
|
mcpContext
|
|
);
|
|
|
|
expect(response.success).toBe(true);
|
|
const data = response.data as any;
|
|
|
|
expect(data.executions.length).toBeLessThanOrEqual(50);
|
|
});
|
|
|
|
it('should respect limit=100 (max)', async () => {
|
|
const response = await handleListExecutions(
|
|
{ limit: 100 },
|
|
mcpContext
|
|
);
|
|
|
|
expect(response.success).toBe(true);
|
|
const data = response.data as any;
|
|
|
|
expect(data.executions.length).toBeLessThanOrEqual(100);
|
|
});
|
|
});
|
|
|
|
// ======================================================================
|
|
// Include Execution Data
|
|
// ======================================================================
|
|
|
|
describe('Include Execution Data', () => {
|
|
it('should exclude execution data by default', async () => {
|
|
const response = await handleListExecutions(
|
|
{ limit: 5 },
|
|
mcpContext
|
|
);
|
|
|
|
expect(response.success).toBe(true);
|
|
const data = response.data as any;
|
|
|
|
expect(Array.isArray(data.executions)).toBe(true);
|
|
// By default, should not include full execution data
|
|
});
|
|
|
|
it('should include execution data when requested', async () => {
|
|
const response = await handleListExecutions(
|
|
{ limit: 5, includeData: true },
|
|
mcpContext
|
|
);
|
|
|
|
expect(response.success).toBe(true);
|
|
const data = response.data as any;
|
|
|
|
expect(Array.isArray(data.executions)).toBe(true);
|
|
});
|
|
});
|
|
|
|
// ======================================================================
|
|
// Empty Results
|
|
// ======================================================================
|
|
|
|
describe('Empty Results', () => {
|
|
it('should return empty array when no executions match filters', async () => {
|
|
// Use a very restrictive workflowId that likely doesn't exist
|
|
const response = await handleListExecutions(
|
|
{ workflowId: '99999999' },
|
|
mcpContext
|
|
);
|
|
|
|
expect(response.success).toBe(true);
|
|
const data = response.data as any;
|
|
|
|
expect(Array.isArray(data.executions)).toBe(true);
|
|
// May or may not be empty depending on actual data
|
|
});
|
|
});
|
|
|
|
// ======================================================================
|
|
// Response Format Verification
|
|
// ======================================================================
|
|
|
|
describe('Response Format', () => {
|
|
it('should return complete list response structure', async () => {
|
|
const response = await handleListExecutions(
|
|
{ limit: 10 },
|
|
mcpContext
|
|
);
|
|
|
|
expect(response.success).toBe(true);
|
|
const data = response.data as any;
|
|
|
|
// Verify required fields
|
|
expect(data).toHaveProperty('executions');
|
|
expect(Array.isArray(data.executions)).toBe(true);
|
|
expect(data).toHaveProperty('returned');
|
|
expect(data).toHaveProperty('hasMore');
|
|
|
|
// Verify pagination fields when present
|
|
if (data.nextCursor) {
|
|
expect(typeof data.nextCursor).toBe('string');
|
|
}
|
|
|
|
// Verify execution structure if any executions returned
|
|
if (data.executions.length > 0) {
|
|
const execution = data.executions[0];
|
|
expect(execution).toHaveProperty('id');
|
|
|
|
if (execution.status) {
|
|
expect(['success', 'error', 'running', 'waiting']).toContain(execution.status);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
});
|