From 08e906739f8751a937109c3009bca0b500ddac9b Mon Sep 17 00:00:00 2001 From: czlonkowski <56956555+czlonkowski@users.noreply.github.com> Date: Sat, 4 Oct 2025 23:57:08 +0200 Subject: [PATCH] fix: resolve type errors from tags parameter change MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixed type errors caused by changing WorkflowListParams.tags from string[] to string: 1. cleanup-helpers.ts: Changed tags: [tag] to tags: tag (line 221) 2. n8n-api-client.test.ts: Changed tags: ['test'] to tags: 'test,production' (line 384) 3. Added unit tests for handleDeleteWorkflow and handleListWorkflows (100% coverage) All tests pass, lint clean. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../n8n-api/utils/cleanup-helpers.ts | 2 +- tests/unit/mcp/handlers-n8n-manager.test.ts | 157 ++++++++++++++++++ tests/unit/services/n8n-api-client.test.ts | 6 +- 3 files changed, 161 insertions(+), 4 deletions(-) diff --git a/tests/integration/n8n-api/utils/cleanup-helpers.ts b/tests/integration/n8n-api/utils/cleanup-helpers.ts index f2dbe9d..b3e02fb 100644 --- a/tests/integration/n8n-api/utils/cleanup-helpers.ts +++ b/tests/integration/n8n-api/utils/cleanup-helpers.ts @@ -218,7 +218,7 @@ export async function cleanupWorkflowsByTag(tag: string): Promise { try { const response = await client.listWorkflows({ - tags: tag ? [tag] : undefined, + tags: tag || undefined, limit: 100, excludePinnedData: true }); diff --git a/tests/unit/mcp/handlers-n8n-manager.test.ts b/tests/unit/mcp/handlers-n8n-manager.test.ts index c587ab6..0201bc1 100644 --- a/tests/unit/mcp/handlers-n8n-manager.test.ts +++ b/tests/unit/mcp/handlers-n8n-manager.test.ts @@ -723,6 +723,66 @@ describe('handlers-n8n-manager', () => { }); }); + describe('handleDeleteWorkflow', () => { + it('should delete workflow successfully', async () => { + const testWorkflow = createTestWorkflow(); + mockApiClient.deleteWorkflow.mockResolvedValue(testWorkflow); + + const result = await handlers.handleDeleteWorkflow({ id: 'test-workflow-id' }); + + expect(result).toEqual({ + success: true, + data: testWorkflow, + message: 'Workflow test-workflow-id deleted successfully', + }); + expect(mockApiClient.deleteWorkflow).toHaveBeenCalledWith('test-workflow-id'); + }); + + it('should handle invalid input', async () => { + const result = await handlers.handleDeleteWorkflow({ notId: 'test' }); + + expect(result.success).toBe(false); + expect(result.error).toBe('Invalid input'); + expect(result.details).toHaveProperty('errors'); + }); + + it('should handle N8nApiError', async () => { + const apiError = new N8nNotFoundError('Workflow', 'non-existent-id'); + mockApiClient.deleteWorkflow.mockRejectedValue(apiError); + + const result = await handlers.handleDeleteWorkflow({ id: 'non-existent-id' }); + + expect(result).toEqual({ + success: false, + error: 'Workflow with ID non-existent-id not found', + code: 'NOT_FOUND', + }); + }); + + it('should handle generic errors', async () => { + const genericError = new Error('Database connection failed'); + mockApiClient.deleteWorkflow.mockRejectedValue(genericError); + + const result = await handlers.handleDeleteWorkflow({ id: 'test-workflow-id' }); + + expect(result).toEqual({ + success: false, + error: 'Database connection failed', + }); + }); + + it('should handle API not configured error', async () => { + vi.mocked(getN8nApiConfig).mockReturnValue(null); + + const result = await handlers.handleDeleteWorkflow({ id: 'test-workflow-id' }); + + expect(result).toEqual({ + success: false, + error: 'n8n API not configured. Please set N8N_API_URL and N8N_API_KEY environment variables.', + }); + }); + }); + describe('handleListWorkflows', () => { it('should list workflows with minimal data', async () => { const workflows = [ @@ -770,6 +830,103 @@ describe('handlers-n8n-manager', () => { }, }); }); + + it('should handle invalid input with ZodError', async () => { + const result = await handlers.handleListWorkflows({ + limit: 'invalid', // Should be a number + }); + + expect(result.success).toBe(false); + expect(result.error).toBe('Invalid input'); + expect(result.details).toHaveProperty('errors'); + }); + + it('should handle N8nApiError', async () => { + const apiError = new N8nAuthenticationError('Invalid API key'); + mockApiClient.listWorkflows.mockRejectedValue(apiError); + + const result = await handlers.handleListWorkflows({}); + + expect(result).toEqual({ + success: false, + error: 'Failed to authenticate with n8n. Please check your API key.', + code: 'AUTHENTICATION_ERROR', + }); + }); + + it('should handle generic errors', async () => { + const genericError = new Error('Network timeout'); + mockApiClient.listWorkflows.mockRejectedValue(genericError); + + const result = await handlers.handleListWorkflows({}); + + expect(result).toEqual({ + success: false, + error: 'Network timeout', + }); + }); + + it('should handle workflows without isArchived field gracefully', async () => { + const workflows = [ + createTestWorkflow({ id: 'wf1', name: 'Workflow 1' }), + ]; + // Remove isArchived field to test undefined handling + delete (workflows[0] as any).isArchived; + + mockApiClient.listWorkflows.mockResolvedValue({ + data: workflows, + nextCursor: null, + }); + + const result = await handlers.handleListWorkflows({}); + + expect(result.success).toBe(true); + expect(result.data.workflows[0]).toHaveProperty('isArchived'); + }); + + it('should convert tags array to comma-separated string', async () => { + const workflows = [ + createTestWorkflow({ id: 'wf1', name: 'Workflow 1', tags: ['tag1', 'tag2'] }), + ]; + + mockApiClient.listWorkflows.mockResolvedValue({ + data: workflows, + nextCursor: null, + }); + + const result = await handlers.handleListWorkflows({ + tags: ['production', 'active'], + }); + + expect(result.success).toBe(true); + expect(mockApiClient.listWorkflows).toHaveBeenCalledWith( + expect.objectContaining({ + tags: 'production,active', + }) + ); + }); + + it('should handle empty tags array', async () => { + const workflows = [ + createTestWorkflow({ id: 'wf1', name: 'Workflow 1' }), + ]; + + mockApiClient.listWorkflows.mockResolvedValue({ + data: workflows, + nextCursor: null, + }); + + const result = await handlers.handleListWorkflows({ + tags: [], + }); + + expect(result.success).toBe(true); + expect(mockApiClient.listWorkflows).toHaveBeenCalledWith( + expect.objectContaining({ + tags: undefined, + }) + ); + }); }); describe('handleValidateWorkflow', () => { diff --git a/tests/unit/services/n8n-api-client.test.ts b/tests/unit/services/n8n-api-client.test.ts index d112086..22077cb 100644 --- a/tests/unit/services/n8n-api-client.test.ts +++ b/tests/unit/services/n8n-api-client.test.ts @@ -381,12 +381,12 @@ describe('N8nApiClient', () => { }); it('should list workflows with custom params', async () => { - const params = { limit: 10, active: true, tags: ['test'] }; + const params = { limit: 10, active: true, tags: 'test,production' }; const response = { data: [], nextCursor: null }; mockAxiosInstance.get.mockResolvedValue({ data: response }); - + const result = await client.listWorkflows(params); - + expect(mockAxiosInstance.get).toHaveBeenCalledWith('/workflows', { params }); expect(result).toEqual(response); });