diff --git a/tests/integration/n8n-api/system/diagnostic.test.ts b/tests/integration/n8n-api/system/diagnostic.test.ts new file mode 100644 index 0000000..986447d --- /dev/null +++ b/tests/integration/n8n-api/system/diagnostic.test.ts @@ -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'); + }); + }); +}); diff --git a/tests/integration/n8n-api/system/health-check.test.ts b/tests/integration/n8n-api/system/health-check.test.ts new file mode 100644 index 0000000..f8d460f --- /dev/null +++ b/tests/integration/n8n-api/system/health-check.test.ts @@ -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(); + } + }); + }); + }); +}); diff --git a/tests/integration/n8n-api/system/list-tools.test.ts b/tests/integration/n8n-api/system/list-tools.test.ts new file mode 100644 index 0000000..1e37726 --- /dev/null +++ b/tests/integration/n8n-api/system/list-tools.test.ts @@ -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); + }); + }); +});