feat: implement Phase 8 integration tests for system tools

Implement comprehensive integration tests for 3 system tool handlers:
- handleHealthCheck (3 tests): API connectivity, version checking, feature availability
- handleListAvailableTools (7 tests): Tool discovery by category, configuration status, API limitations
- handleDiagnostic (9 tests): Environment checks, API status, tools availability, verbose mode

All 19 tests passing against real n8n instance.

Coverage:
- Health check: API availability verification, version information, feature discovery
- Tool listing: All categories (Workflow Management, Execution Management, System), configuration details
- Diagnostics: Environment variables, API connectivity, tool availability, troubleshooting steps, verbose debug mode

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
czlonkowski
2025-10-05 10:25:41 +02:00
parent bd8a7f68ac
commit 69f3a31d41
3 changed files with 584 additions and 0 deletions

View File

@@ -0,0 +1,268 @@
/**
* Integration Tests: handleDiagnostic
*
* Tests system diagnostic functionality.
* Covers environment checks, API status, and verbose mode.
*/
import { describe, it, expect, beforeEach } from 'vitest';
import { createMcpContext } from '../utils/mcp-context';
import { InstanceContext } from '../../../../src/types/instance-context';
import { handleDiagnostic } from '../../../../src/mcp/handlers-n8n-manager';
describe('Integration: handleDiagnostic', () => {
let mcpContext: InstanceContext;
beforeEach(() => {
mcpContext = createMcpContext();
});
// ======================================================================
// Basic Diagnostic
// ======================================================================
describe('Basic Diagnostic', () => {
it('should run basic diagnostic check', async () => {
const response = await handleDiagnostic(
{ params: { arguments: {} } },
mcpContext
);
expect(response.success).toBe(true);
expect(response.data).toBeDefined();
const data = response.data as any;
// Verify core diagnostic fields
expect(data).toHaveProperty('timestamp');
expect(data).toHaveProperty('environment');
expect(data).toHaveProperty('apiConfiguration');
expect(data).toHaveProperty('toolsAvailability');
expect(data).toHaveProperty('troubleshooting');
// Verify timestamp format
expect(typeof data.timestamp).toBe('string');
const timestamp = new Date(data.timestamp);
expect(timestamp.toString()).not.toBe('Invalid Date');
});
it('should include environment variables', async () => {
const response = await handleDiagnostic(
{ params: { arguments: {} } },
mcpContext
);
const data = response.data as any;
expect(data.environment).toBeDefined();
expect(data.environment).toHaveProperty('N8N_API_URL');
expect(data.environment).toHaveProperty('N8N_API_KEY');
expect(data.environment).toHaveProperty('NODE_ENV');
expect(data.environment).toHaveProperty('MCP_MODE');
// API key should be masked
if (data.environment.N8N_API_KEY) {
expect(data.environment.N8N_API_KEY).toBe('***configured***');
}
});
it('should check API configuration and connectivity', async () => {
const response = await handleDiagnostic(
{ params: { arguments: {} } },
mcpContext
);
const data = response.data as any;
expect(data.apiConfiguration).toBeDefined();
expect(data.apiConfiguration).toHaveProperty('configured');
expect(data.apiConfiguration).toHaveProperty('status');
// In test environment, API should be configured
expect(data.apiConfiguration.configured).toBe(true);
// Verify API status
const status = data.apiConfiguration.status;
expect(status).toHaveProperty('configured');
expect(status).toHaveProperty('connected');
// Should successfully connect to n8n API
expect(status.connected).toBe(true);
// If connected, should have version info
if (status.connected) {
expect(status).toHaveProperty('version');
}
// Config details should be present when configured
if (data.apiConfiguration.configured) {
expect(data.apiConfiguration).toHaveProperty('config');
expect(data.apiConfiguration.config).toHaveProperty('baseUrl');
expect(data.apiConfiguration.config).toHaveProperty('timeout');
expect(data.apiConfiguration.config).toHaveProperty('maxRetries');
}
});
it('should report tools availability', async () => {
const response = await handleDiagnostic(
{ params: { arguments: {} } },
mcpContext
);
const data = response.data as any;
expect(data.toolsAvailability).toBeDefined();
expect(data.toolsAvailability).toHaveProperty('documentationTools');
expect(data.toolsAvailability).toHaveProperty('managementTools');
expect(data.toolsAvailability).toHaveProperty('totalAvailable');
// Documentation tools should always be available
const docTools = data.toolsAvailability.documentationTools;
expect(docTools.count).toBeGreaterThan(0);
expect(docTools.enabled).toBe(true);
expect(docTools.description).toBeDefined();
// Management tools should be available when API configured
const mgmtTools = data.toolsAvailability.managementTools;
expect(mgmtTools).toHaveProperty('count');
expect(mgmtTools).toHaveProperty('enabled');
expect(mgmtTools).toHaveProperty('description');
// In test environment, management tools should be enabled
expect(mgmtTools.enabled).toBe(true);
expect(mgmtTools.count).toBeGreaterThan(0);
// Total should be sum of both
expect(data.toolsAvailability.totalAvailable).toBe(
docTools.count + mgmtTools.count
);
});
it('should include troubleshooting information', async () => {
const response = await handleDiagnostic(
{ params: { arguments: {} } },
mcpContext
);
const data = response.data as any;
expect(data.troubleshooting).toBeDefined();
expect(data.troubleshooting).toHaveProperty('steps');
expect(data.troubleshooting).toHaveProperty('documentation');
// Troubleshooting steps should be an array
expect(Array.isArray(data.troubleshooting.steps)).toBe(true);
expect(data.troubleshooting.steps.length).toBeGreaterThan(0);
// Documentation link should be present
expect(typeof data.troubleshooting.documentation).toBe('string');
expect(data.troubleshooting.documentation).toContain('https://');
});
});
// ======================================================================
// Verbose Mode
// ======================================================================
describe('Verbose Mode', () => {
it('should include additional debug info in verbose mode', async () => {
const response = await handleDiagnostic(
{ params: { arguments: { verbose: true } } },
mcpContext
);
expect(response.success).toBe(true);
const data = response.data as any;
// Verbose mode should add debug section
expect(data).toHaveProperty('debug');
expect(data.debug).toBeDefined();
// Verify debug information
expect(data.debug).toHaveProperty('processEnv');
expect(data.debug).toHaveProperty('nodeVersion');
expect(data.debug).toHaveProperty('platform');
expect(data.debug).toHaveProperty('workingDirectory');
// Process env should list relevant environment variables
expect(Array.isArray(data.debug.processEnv)).toBe(true);
// Node version should be a string
expect(typeof data.debug.nodeVersion).toBe('string');
expect(data.debug.nodeVersion).toMatch(/^v\d+\.\d+\.\d+/);
// Platform should be a string (linux, darwin, win32, etc.)
expect(typeof data.debug.platform).toBe('string');
expect(data.debug.platform.length).toBeGreaterThan(0);
// Working directory should be a path
expect(typeof data.debug.workingDirectory).toBe('string');
expect(data.debug.workingDirectory.length).toBeGreaterThan(0);
});
it('should not include debug info when verbose is false', async () => {
const response = await handleDiagnostic(
{ params: { arguments: { verbose: false } } },
mcpContext
);
expect(response.success).toBe(true);
const data = response.data as any;
// Debug section should not be present
expect(data.debug).toBeUndefined();
});
it('should not include debug info by default', async () => {
const response = await handleDiagnostic(
{ params: { arguments: {} } },
mcpContext
);
expect(response.success).toBe(true);
const data = response.data as any;
// Debug section should not be present when verbose not specified
expect(data.debug).toBeUndefined();
});
});
// ======================================================================
// Response Format Verification
// ======================================================================
describe('Response Format', () => {
it('should return complete diagnostic response structure', async () => {
const response = await handleDiagnostic(
{ params: { arguments: {} } },
mcpContext
);
expect(response.success).toBe(true);
expect(response.data).toBeDefined();
const data = response.data as any;
// Verify all required fields
const requiredFields = [
'timestamp',
'environment',
'apiConfiguration',
'toolsAvailability',
'troubleshooting'
];
requiredFields.forEach(field => {
expect(data).toHaveProperty(field);
expect(data[field]).toBeDefined();
});
// Verify data types
expect(typeof data.timestamp).toBe('string');
expect(typeof data.environment).toBe('object');
expect(typeof data.apiConfiguration).toBe('object');
expect(typeof data.toolsAvailability).toBe('object');
expect(typeof data.troubleshooting).toBe('object');
});
});
});

View File

@@ -0,0 +1,109 @@
/**
* Integration Tests: handleHealthCheck
*
* Tests API health check against a real n8n instance.
* Covers connectivity verification and feature availability.
*/
import { describe, it, expect, beforeEach } from 'vitest';
import { createMcpContext } from '../utils/mcp-context';
import { InstanceContext } from '../../../../src/types/instance-context';
import { handleHealthCheck } from '../../../../src/mcp/handlers-n8n-manager';
describe('Integration: handleHealthCheck', () => {
let mcpContext: InstanceContext;
beforeEach(() => {
mcpContext = createMcpContext();
});
// ======================================================================
// Successful Health Check
// ======================================================================
describe('API Available', () => {
it('should successfully check n8n API health', async () => {
const response = await handleHealthCheck(mcpContext);
expect(response.success).toBe(true);
expect(response.data).toBeDefined();
const data = response.data as any;
// Verify required fields
expect(data).toHaveProperty('status');
expect(data).toHaveProperty('apiUrl');
expect(data).toHaveProperty('mcpVersion');
// Status should be a string (e.g., "ok", "healthy")
if (data.status) {
expect(typeof data.status).toBe('string');
}
// API URL should match configuration
expect(data.apiUrl).toBeDefined();
expect(typeof data.apiUrl).toBe('string');
// MCP version should be defined
expect(data.mcpVersion).toBeDefined();
expect(typeof data.mcpVersion).toBe('string');
});
it('should include feature availability information', async () => {
const response = await handleHealthCheck(mcpContext);
expect(response.success).toBe(true);
const data = response.data as any;
// Check for feature information
// Note: Features may vary by n8n instance configuration
if (data.features) {
expect(typeof data.features).toBe('object');
}
// Check for version information
if (data.n8nVersion) {
expect(typeof data.n8nVersion).toBe('string');
}
if (data.supportedN8nVersion) {
expect(typeof data.supportedN8nVersion).toBe('string');
}
// Should include version note for AI agents
if (data.versionNote) {
expect(typeof data.versionNote).toBe('string');
expect(data.versionNote).toContain('version');
}
});
});
// ======================================================================
// Response Format Verification
// ======================================================================
describe('Response Format', () => {
it('should return complete health check response structure', async () => {
const response = await handleHealthCheck(mcpContext);
expect(response.success).toBe(true);
expect(response.data).toBeDefined();
const data = response.data as any;
// Verify all expected fields are present
const expectedFields = ['status', 'apiUrl', 'mcpVersion'];
expectedFields.forEach(field => {
expect(data).toHaveProperty(field);
});
// Optional fields that may be present
const optionalFields = ['instanceId', 'n8nVersion', 'features', 'supportedN8nVersion', 'versionNote'];
optionalFields.forEach(field => {
if (data[field] !== undefined) {
expect(data[field]).not.toBeNull();
}
});
});
});
});

View File

@@ -0,0 +1,207 @@
/**
* Integration Tests: handleListAvailableTools
*
* Tests tool listing functionality.
* Covers tool discovery and configuration status.
*/
import { describe, it, expect, beforeEach } from 'vitest';
import { createMcpContext } from '../utils/mcp-context';
import { InstanceContext } from '../../../../src/types/instance-context';
import { handleListAvailableTools } from '../../../../src/mcp/handlers-n8n-manager';
describe('Integration: handleListAvailableTools', () => {
let mcpContext: InstanceContext;
beforeEach(() => {
mcpContext = createMcpContext();
});
// ======================================================================
// List All Tools
// ======================================================================
describe('Tool Listing', () => {
it('should list all available tools organized by category', async () => {
const response = await handleListAvailableTools(mcpContext);
expect(response.success).toBe(true);
expect(response.data).toBeDefined();
const data = response.data as any;
// Verify tools array exists
expect(data).toHaveProperty('tools');
expect(Array.isArray(data.tools)).toBe(true);
expect(data.tools.length).toBeGreaterThan(0);
// Verify tool categories
const categories = data.tools.map((cat: any) => cat.category);
expect(categories).toContain('Workflow Management');
expect(categories).toContain('Execution Management');
expect(categories).toContain('System');
// Verify each category has tools
data.tools.forEach((category: any) => {
expect(category).toHaveProperty('category');
expect(category).toHaveProperty('tools');
expect(Array.isArray(category.tools)).toBe(true);
expect(category.tools.length).toBeGreaterThan(0);
// Verify each tool has required fields
category.tools.forEach((tool: any) => {
expect(tool).toHaveProperty('name');
expect(tool).toHaveProperty('description');
expect(typeof tool.name).toBe('string');
expect(typeof tool.description).toBe('string');
});
});
});
it('should include API configuration status', async () => {
const response = await handleListAvailableTools(mcpContext);
expect(response.success).toBe(true);
const data = response.data as any;
// Verify configuration status
expect(data).toHaveProperty('apiConfigured');
expect(typeof data.apiConfigured).toBe('boolean');
// Since tests run with API configured, should be true
expect(data.apiConfigured).toBe(true);
// Verify configuration details are present when configured
if (data.apiConfigured) {
expect(data).toHaveProperty('configuration');
expect(data.configuration).toBeDefined();
expect(data.configuration).toHaveProperty('apiUrl');
expect(data.configuration).toHaveProperty('timeout');
expect(data.configuration).toHaveProperty('maxRetries');
}
});
it('should include API limitations information', async () => {
const response = await handleListAvailableTools(mcpContext);
expect(response.success).toBe(true);
const data = response.data as any;
// Verify limitations are documented
expect(data).toHaveProperty('limitations');
expect(Array.isArray(data.limitations)).toBe(true);
expect(data.limitations.length).toBeGreaterThan(0);
// Verify limitations are informative strings
data.limitations.forEach((limitation: string) => {
expect(typeof limitation).toBe('string');
expect(limitation.length).toBeGreaterThan(0);
});
// Common known limitations
const limitationsText = data.limitations.join(' ');
expect(limitationsText).toContain('Cannot activate');
expect(limitationsText).toContain('Cannot execute workflows directly');
});
});
// ======================================================================
// Workflow Management Tools
// ======================================================================
describe('Workflow Management Tools', () => {
it('should include all workflow management tools', async () => {
const response = await handleListAvailableTools(mcpContext);
const data = response.data as any;
const workflowCategory = data.tools.find((cat: any) => cat.category === 'Workflow Management');
expect(workflowCategory).toBeDefined();
const toolNames = workflowCategory.tools.map((t: any) => t.name);
// Core workflow tools
expect(toolNames).toContain('n8n_create_workflow');
expect(toolNames).toContain('n8n_get_workflow');
expect(toolNames).toContain('n8n_update_workflow');
expect(toolNames).toContain('n8n_delete_workflow');
expect(toolNames).toContain('n8n_list_workflows');
// Enhanced workflow tools
expect(toolNames).toContain('n8n_get_workflow_details');
expect(toolNames).toContain('n8n_get_workflow_structure');
expect(toolNames).toContain('n8n_get_workflow_minimal');
expect(toolNames).toContain('n8n_validate_workflow');
expect(toolNames).toContain('n8n_autofix_workflow');
});
});
// ======================================================================
// Execution Management Tools
// ======================================================================
describe('Execution Management Tools', () => {
it('should include all execution management tools', async () => {
const response = await handleListAvailableTools(mcpContext);
const data = response.data as any;
const executionCategory = data.tools.find((cat: any) => cat.category === 'Execution Management');
expect(executionCategory).toBeDefined();
const toolNames = executionCategory.tools.map((t: any) => t.name);
expect(toolNames).toContain('n8n_trigger_webhook_workflow');
expect(toolNames).toContain('n8n_get_execution');
expect(toolNames).toContain('n8n_list_executions');
expect(toolNames).toContain('n8n_delete_execution');
});
});
// ======================================================================
// System Tools
// ======================================================================
describe('System Tools', () => {
it('should include system tools', async () => {
const response = await handleListAvailableTools(mcpContext);
const data = response.data as any;
const systemCategory = data.tools.find((cat: any) => cat.category === 'System');
expect(systemCategory).toBeDefined();
const toolNames = systemCategory.tools.map((t: any) => t.name);
expect(toolNames).toContain('n8n_health_check');
expect(toolNames).toContain('n8n_list_available_tools');
});
});
// ======================================================================
// Response Format Verification
// ======================================================================
describe('Response Format', () => {
it('should return complete tool list response structure', async () => {
const response = await handleListAvailableTools(mcpContext);
expect(response.success).toBe(true);
expect(response.data).toBeDefined();
const data = response.data as any;
// Verify all required fields
expect(data).toHaveProperty('tools');
expect(data).toHaveProperty('apiConfigured');
expect(data).toHaveProperty('limitations');
// Verify optional configuration field
if (data.apiConfigured) {
expect(data).toHaveProperty('configuration');
}
// Verify data types
expect(Array.isArray(data.tools)).toBe(true);
expect(typeof data.apiConfigured).toBe('boolean');
expect(Array.isArray(data.limitations)).toBe(true);
});
});
});