list-executions.test.tsβ’8.11 kB
/**
* 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);
}
}
});
});
});