diff --git a/docs/local/integration-testing-plan.md b/docs/local/integration-testing-plan.md index cbc2139..31429d4 100644 --- a/docs/local/integration-testing-plan.md +++ b/docs/local/integration-testing-plan.md @@ -16,7 +16,61 @@ - Documented actual n8n API behavior (validation at execution time, not creation time) - Test file: `tests/integration/n8n-api/workflows/create-workflow.test.ts` (484 lines) -**Next Phase**: Phase 3 - Workflow Retrieval Tests +**Phase 3: Workflow Retrieval Tests** ✅ **COMPLETE** (October 3, 2025) +- 11 test scenarios implemented and passing +- All MCP retrieval handlers tested: handleGetWorkflow, handleGetWorkflowDetails, handleGetWorkflowStructure, handleGetWorkflowMinimal +- Test files: + - `get-workflow.test.ts` (3 scenarios) + - `get-workflow-details.test.ts` (4 scenarios) + - `get-workflow-structure.test.ts` (2 scenarios) + - `get-workflow-minimal.test.ts` (2 scenarios) + +**Phase 4: Workflow Update Tests** ✅ **COMPLETE** (October 4, 2025) +- 42 test scenarios implemented and passing +- Enhanced settings filtering (whitelist approach) to enable updates while maintaining Issue #248 protection +- All update operations tested: + - Full workflow updates: 7 scenarios (update-workflow.test.ts) + - Partial/diff-based updates: 32 scenarios covering all 15 operations (update-partial-workflow.test.ts) + - Validation error scenarios: 3 scenarios +- Critical fixes: + - Settings filtering uses OpenAPI spec whitelist (filters callerPolicy, preserves safe properties) + - All tests comply with n8n API requirements (name, nodes, connections, settings fields) + - Removed invalid "Update Connections" test (empty connections invalid for multi-node workflows) +- Version 2.15.4 released with comprehensive CHANGELOG entry + +**Phase 5: Workflow Management Tests** ✅ **COMPLETE** (October 4, 2025) +- 16 test scenarios implemented and passing +- All workflow management operations tested: + - Delete workflow: 3 scenarios (delete-workflow.test.ts) + - List workflows: 13 scenarios (list-workflows.test.ts) +- Critical API compliance fixes: + - handleDeleteWorkflow: Now returns deleted workflow data (per n8n API spec) + - handleListWorkflows: Fixed tags parameter format (array → CSV string conversion) + - N8nApiClient.deleteWorkflow: Return type corrected (void → Workflow) + - WorkflowListParams.tags: Type corrected (string[] → string per n8n OpenAPI spec) +- Unit test coverage: Added 9 unit tests for handler coverage (100% coverage achieved) +- n8n-mcp-tester validation: All tools tested and working correctly in production +- Version 2.15.5 released with comprehensive CHANGELOG entry +- Test results: 71/71 integration tests passing (Phase 1-5 complete) + +**Phase 6A: Workflow Validation Tests** ✅ **COMPLETE** (October 5, 2025) +- 12 test scenarios implemented and passing +- NodeRepository utility created for tests requiring node validation +- All validation profiles tested: strict, runtime, ai-friendly, minimal +- Test coverage: + - Valid workflows across all 4 profiles (4 scenarios) + - Invalid workflow detection (2 scenarios - bad node types, missing connections) + - Selective validation (3 scenarios - nodes only, connections only, expressions only) + - Error handling (2 scenarios - non-existent workflow, invalid profile) + - Response format verification (1 scenario) +- Critical discoveries: + - Response only includes errors/warnings fields when they exist (not empty arrays) + - Field name is errorCount, not totalErrors + - Tests require NodeRepository instance (added singleton utility) +- Test file: validate-workflow.test.ts (431 lines) +- Test results: 83/83 integration tests passing (Phase 1-5, 6A complete) + +**Next Phase**: Phase 6B - Workflow Autofix Tests --- @@ -912,13 +966,35 @@ const stats = detailsResponse.data.executionStats; --- -### Phase 6: Validation & Autofix Tests (P2) +### Phase 6A: Workflow Validation Tests (P2) ✅ COMPLETE -**Branch**: `feat/integration-tests-validation` +**Branch**: `feat/integration-tests-phase-6` **Files**: -- `validate-workflow.test.ts` (16 scenarios: 4 profiles × 4 validation types) -- `autofix-workflow.test.ts` (20+ scenarios: 5 fix types × confidence levels) +- ✅ `tests/integration/n8n-api/utils/node-repository.ts` - NodeRepository singleton for validation tests +- ✅ `validate-workflow.test.ts` (12 scenarios: 4 profiles + invalid detection + selective validation + error handling) + +**Implementation Notes**: +- Created NodeRepository utility since handleValidateWorkflow requires repository parameter +- Tests cover all 4 validation profiles (strict, runtime, ai-friendly, minimal) +- Invalid workflow detection tests (bad node types, missing connections) +- Selective validation tests (nodes only, connections only, expressions only) +- Response structure correctly handles conditional errors/warnings fields + +### Phase 6B: Workflow Autofix Tests (P2) + +**Branch**: `feat/integration-tests-phase-6b` (or continue on `feat/integration-tests-phase-6`) + +**Files**: +- `autofix-workflow.test.ts` (15-20 scenarios: 5 fix types × modes × confidence levels) + +**Test Coverage Required**: +- 5 fix types: expression-format, typeversion-correction, error-output-config, node-type-correction, webhook-missing-path +- Preview mode (applyFixes: false) vs Apply mode (applyFixes: true) +- Confidence threshold filtering (high, medium, low) +- maxFixes parameter limiting +- Multiple fix types in single workflow +- No fixes available scenario --- @@ -1090,9 +1166,9 @@ jobs: - ✅ All tests passing against real n8n instance ### Overall Project (In Progress) -- ⏳ All 17 handlers have integration tests (1 of 17 complete) -- ⏳ All operations/parameters covered (15 of 150+ scenarios complete) -- ✅ Tests run successfully locally (Phase 2 verified) +- ⏳ All 17 handlers have integration tests (10 of 17 complete) +- ⏳ All operations/parameters covered (83 of 150+ scenarios complete) +- ✅ Tests run successfully locally (Phases 1-6A verified) - ⏳ Tests run successfully in CI (pending Phase 9) - ✅ No manual cleanup required (automatic) - ✅ Test coverage catches P0-level bugs (verified in Phase 2) @@ -1106,15 +1182,16 @@ jobs: - **Phase 1 (Foundation)**: ✅ COMPLETE (October 3, 2025) - **Phase 2 (Workflow Creation)**: ✅ COMPLETE (October 3, 2025) -- **Phase 3 (Retrieval)**: 1 day -- **Phase 4 (Updates)**: 2-3 days (15 operations) -- **Phase 5 (Management)**: 1 day -- **Phase 6 (Validation)**: 2 days +- **Phase 3 (Retrieval)**: ✅ COMPLETE (October 3, 2025) +- **Phase 4 (Updates)**: ✅ COMPLETE (October 4, 2025) +- **Phase 5 (Management)**: ✅ COMPLETE (October 4, 2025) +- **Phase 6A (Validation)**: ✅ COMPLETE (October 5, 2025) +- **Phase 6B (Autofix)**: 1 day - **Phase 7 (Executions)**: 2 days - **Phase 8 (System)**: 1 day - **Phase 9 (CI/CD)**: 1 day -**Total**: 2 days complete (~4-6 hours actual), ~12-16 days remaining +**Total**: 5.5 days complete, ~5 days remaining --- diff --git a/tests/integration/n8n-api/workflows/autofix-workflow.test.ts b/tests/integration/n8n-api/workflows/autofix-workflow.test.ts new file mode 100644 index 0000000..e69579f --- /dev/null +++ b/tests/integration/n8n-api/workflows/autofix-workflow.test.ts @@ -0,0 +1,851 @@ +/** + * Integration Tests: handleAutofixWorkflow + * + * Tests workflow autofix against a real n8n instance. + * Covers fix types, confidence levels, preview/apply modes, and error handling. + */ + +import { describe, it, expect, beforeEach, afterEach, afterAll } from 'vitest'; +import { createTestContext, TestContext, createTestWorkflowName } from '../utils/test-context'; +import { getTestN8nClient } from '../utils/n8n-client'; +import { N8nApiClient } from '../../../../src/services/n8n-api-client'; +import { cleanupOrphanedWorkflows } from '../utils/cleanup-helpers'; +import { createMcpContext } from '../utils/mcp-context'; +import { InstanceContext } from '../../../../src/types/instance-context'; +import { handleAutofixWorkflow } from '../../../../src/mcp/handlers-n8n-manager'; +import { getNodeRepository } from '../utils/node-repository'; +import { NodeRepository } from '../../../../src/database/node-repository'; + +describe('Integration: handleAutofixWorkflow', () => { + let context: TestContext; + let client: N8nApiClient; + let mcpContext: InstanceContext; + let repository: NodeRepository; + + beforeEach(async () => { + context = createTestContext(); + client = getTestN8nClient(); + mcpContext = createMcpContext(); + repository = await getNodeRepository(); + }); + + afterEach(async () => { + await context.cleanup(); + }); + + afterAll(async () => { + if (!process.env.CI) { + await cleanupOrphanedWorkflows(); + } + }); + + // ====================================================================== + // Preview Mode (applyFixes: false) + // ====================================================================== + + describe('Preview Mode', () => { + it('should preview fixes without applying them (expression-format)', async () => { + // Create workflow with expression format issues + const workflow = { + name: createTestWorkflowName('Autofix - Preview Expression'), + nodes: [ + { + id: 'webhook-1', + name: 'Webhook', + type: 'n8n-nodes-base.webhook', + typeVersion: 2, + position: [250, 300] as [number, number], + parameters: { + httpMethod: 'GET', + path: 'test' + } + }, + { + id: 'set-1', + name: 'Set', + type: 'n8n-nodes-base.set', + typeVersion: 3.4, + position: [450, 300] as [number, number], + parameters: { + // Bad expression format (missing {{}}) + assignments: { + assignments: [ + { + id: '1', + name: 'value', + value: '$json.data', // Should be {{ $json.data }} + type: 'string' + } + ] + } + } + } + ], + connections: { + Webhook: { + main: [[{ node: 'Set', type: 'main', index: 0 }]] + } + }, + settings: {}, + tags: ['mcp-integration-test'] + }; + + const created = await client.createWorkflow(workflow); + context.trackWorkflow(created.id!); + + // Preview fixes (applyFixes: false) + const response = await handleAutofixWorkflow( + { + id: created.id, + applyFixes: false + }, + repository, + mcpContext + ); + + expect(response.success).toBe(true); + const data = response.data as any; + + // If fixes are available, should be in preview mode + if (data.fixesAvailable && data.fixesAvailable > 0) { + expect(data.preview).toBe(true); + expect(data.fixes).toBeDefined(); + expect(Array.isArray(data.fixes)).toBe(true); + expect(data.summary).toBeDefined(); + expect(data.stats).toBeDefined(); + + // Verify workflow not modified (fetch it back) + const fetched = await client.getWorkflow(created.id!); + expect(fetched.nodes[1].parameters.assignments.assignments[0].value).toBe('$json.data'); + } else { + // No fixes available - that's also a valid result + expect(data.message).toContain('No automatic fixes available'); + } + }); + + it('should preview multiple fix types', async () => { + // Create workflow with multiple issues + const workflow = { + name: createTestWorkflowName('Autofix - Preview Multiple'), + nodes: [ + { + id: 'webhook-1', + name: 'Webhook', + type: 'n8n-nodes-base.webhook', + typeVersion: 1, // Old typeVersion + position: [250, 300] as [number, number], + parameters: { + httpMethod: 'GET' + // Missing path parameter + } + } + ], + connections: {}, + settings: {}, + tags: ['mcp-integration-test'] + }; + + const created = await client.createWorkflow(workflow); + context.trackWorkflow(created.id!); + + const response = await handleAutofixWorkflow( + { + id: created.id, + applyFixes: false + }, + repository, + mcpContext + ); + + expect(response.success).toBe(true); + const data = response.data as any; + + expect(data.preview).toBe(true); + expect(data.fixesAvailable).toBeGreaterThan(0); + }); + }); + + // ====================================================================== + // Apply Mode (applyFixes: true) + // ====================================================================== + + describe('Apply Mode', () => { + it('should apply expression-format fixes', async () => { + const workflow = { + name: createTestWorkflowName('Autofix - Apply Expression'), + nodes: [ + { + id: 'webhook-1', + name: 'Webhook', + type: 'n8n-nodes-base.webhook', + typeVersion: 2, + position: [250, 300] as [number, number], + parameters: { + httpMethod: 'GET', + path: 'test' + } + }, + { + id: 'set-1', + name: 'Set', + type: 'n8n-nodes-base.set', + typeVersion: 3.4, + position: [450, 300] as [number, number], + parameters: { + assignments: { + assignments: [ + { + id: '1', + name: 'value', + value: '$json.data', // Bad format + type: 'string' + } + ] + } + } + } + ], + connections: { + Webhook: { + main: [[{ node: 'Set', type: 'main', index: 0 }]] + } + }, + settings: {}, + tags: ['mcp-integration-test'] + }; + + const created = await client.createWorkflow(workflow); + context.trackWorkflow(created.id!); + + // Apply fixes + const response = await handleAutofixWorkflow( + { + id: created.id, + applyFixes: true, + fixTypes: ['expression-format'] + }, + repository, + mcpContext + ); + + expect(response.success).toBe(true); + const data = response.data as any; + + // If fixes were applied + if (data.fixesApplied && data.fixesApplied > 0) { + expect(data.fixes).toBeDefined(); + expect(data.preview).toBeUndefined(); + + // Verify workflow was actually modified + const fetched = await client.getWorkflow(created.id!); + const setValue = fetched.nodes[1].parameters.assignments.assignments[0].value; + // Expression format should be fixed (depends on what fixes were available) + expect(setValue).toBeDefined(); + } else { + // No fixes available or applied - that's also valid + expect(data.message).toBeDefined(); + } + }); + + it('should apply webhook-missing-path fixes', async () => { + const workflow = { + name: createTestWorkflowName('Autofix - Apply Webhook Path'), + nodes: [ + { + id: 'webhook-1', + name: 'Webhook', + type: 'n8n-nodes-base.webhook', + typeVersion: 2, + position: [250, 300] as [number, number], + parameters: { + httpMethod: 'GET' + // Missing path + } + } + ], + connections: {}, + settings: {}, + tags: ['mcp-integration-test'] + }; + + const created = await client.createWorkflow(workflow); + context.trackWorkflow(created.id!); + + const response = await handleAutofixWorkflow( + { + id: created.id, + applyFixes: true, + fixTypes: ['webhook-missing-path'] + }, + repository, + mcpContext + ); + + expect(response.success).toBe(true); + const data = response.data as any; + + if (data.fixesApplied > 0) { + // Verify path was added + const fetched = await client.getWorkflow(created.id!); + expect(fetched.nodes[0].parameters.path).toBeDefined(); + expect(fetched.nodes[0].parameters.path).toBeTruthy(); + } + }); + }); + + // ====================================================================== + // Fix Type Filtering + // ====================================================================== + + describe('Fix Type Filtering', () => { + it('should only apply specified fix types', async () => { + const workflow = { + name: createTestWorkflowName('Autofix - Filter Fix Types'), + nodes: [ + { + id: 'webhook-1', + name: 'Webhook', + type: 'n8n-nodes-base.webhook', + typeVersion: 1, // Old typeVersion + position: [250, 300] as [number, number], + parameters: { + httpMethod: 'GET' + // Missing path + } + } + ], + connections: {}, + settings: {}, + tags: ['mcp-integration-test'] + }; + + const created = await client.createWorkflow(workflow); + context.trackWorkflow(created.id!); + + // Only request webhook-missing-path fixes (ignore typeversion issues) + const response = await handleAutofixWorkflow( + { + id: created.id, + applyFixes: false, + fixTypes: ['webhook-missing-path'] + }, + repository, + mcpContext + ); + + expect(response.success).toBe(true); + const data = response.data as any; + + // Should only show webhook-missing-path fixes + if (data.fixes && data.fixes.length > 0) { + data.fixes.forEach((fix: any) => { + expect(fix.type).toBe('webhook-missing-path'); + }); + } + }); + + it('should handle multiple fix types filter', async () => { + const workflow = { + name: createTestWorkflowName('Autofix - Multiple Filter'), + nodes: [ + { + id: 'webhook-1', + name: 'Webhook', + type: 'n8n-nodes-base.webhook', + typeVersion: 2, + position: [250, 300] as [number, number], + parameters: { + httpMethod: 'GET', + path: 'test' + } + } + ], + connections: {}, + settings: {}, + tags: ['mcp-integration-test'] + }; + + const created = await client.createWorkflow(workflow); + context.trackWorkflow(created.id!); + + const response = await handleAutofixWorkflow( + { + id: created.id, + applyFixes: false, + fixTypes: ['expression-format', 'webhook-missing-path'] + }, + repository, + mcpContext + ); + + expect(response.success).toBe(true); + }); + }); + + // ====================================================================== + // Confidence Threshold + // ====================================================================== + + describe('Confidence Threshold', () => { + it('should filter fixes by high confidence threshold', async () => { + const workflow = { + name: createTestWorkflowName('Autofix - High Confidence'), + nodes: [ + { + id: 'webhook-1', + name: 'Webhook', + type: 'n8n-nodes-base.webhook', + typeVersion: 2, + position: [250, 300] as [number, number], + parameters: { + httpMethod: 'GET', + path: 'test' + } + } + ], + connections: {}, + settings: {}, + tags: ['mcp-integration-test'] + }; + + const created = await client.createWorkflow(workflow); + context.trackWorkflow(created.id!); + + const response = await handleAutofixWorkflow( + { + id: created.id, + applyFixes: false, + confidenceThreshold: 'high' + }, + repository, + mcpContext + ); + + expect(response.success).toBe(true); + const data = response.data as any; + + // All fixes should be high confidence + if (data.fixes && data.fixes.length > 0) { + data.fixes.forEach((fix: any) => { + expect(fix.confidence).toBe('high'); + }); + } + }); + + it('should include medium and high confidence with medium threshold', async () => { + const workflow = { + name: createTestWorkflowName('Autofix - Medium Confidence'), + nodes: [ + { + id: 'webhook-1', + name: 'Webhook', + type: 'n8n-nodes-base.webhook', + typeVersion: 2, + position: [250, 300] as [number, number], + parameters: { + httpMethod: 'GET', + path: 'test' + } + } + ], + connections: {}, + settings: {}, + tags: ['mcp-integration-test'] + }; + + const created = await client.createWorkflow(workflow); + context.trackWorkflow(created.id!); + + const response = await handleAutofixWorkflow( + { + id: created.id, + applyFixes: false, + confidenceThreshold: 'medium' + }, + repository, + mcpContext + ); + + expect(response.success).toBe(true); + const data = response.data as any; + + // Fixes should be medium or high confidence + if (data.fixes && data.fixes.length > 0) { + data.fixes.forEach((fix: any) => { + expect(['high', 'medium']).toContain(fix.confidence); + }); + } + }); + + it('should include all confidence levels with low threshold', async () => { + const workflow = { + name: createTestWorkflowName('Autofix - Low Confidence'), + nodes: [ + { + id: 'webhook-1', + name: 'Webhook', + type: 'n8n-nodes-base.webhook', + typeVersion: 2, + position: [250, 300] as [number, number], + parameters: { + httpMethod: 'GET', + path: 'test' + } + } + ], + connections: {}, + settings: {}, + tags: ['mcp-integration-test'] + }; + + const created = await client.createWorkflow(workflow); + context.trackWorkflow(created.id!); + + const response = await handleAutofixWorkflow( + { + id: created.id, + applyFixes: false, + confidenceThreshold: 'low' + }, + repository, + mcpContext + ); + + expect(response.success).toBe(true); + }); + }); + + // ====================================================================== + // Max Fixes Parameter + // ====================================================================== + + describe('Max Fixes Parameter', () => { + it('should limit fixes to maxFixes parameter', async () => { + // Create workflow with multiple issues + const workflow = { + name: createTestWorkflowName('Autofix - Max Fixes'), + nodes: [ + { + id: 'webhook-1', + name: 'Webhook', + type: 'n8n-nodes-base.webhook', + typeVersion: 2, + position: [250, 300] as [number, number], + parameters: { + httpMethod: 'GET', + path: 'test' + } + }, + { + id: 'set-1', + name: 'Set 1', + type: 'n8n-nodes-base.set', + typeVersion: 3.4, + position: [450, 300] as [number, number], + parameters: { + assignments: { + assignments: [ + { id: '1', name: 'val1', value: '$json.a', type: 'string' }, + { id: '2', name: 'val2', value: '$json.b', type: 'string' }, + { id: '3', name: 'val3', value: '$json.c', type: 'string' } + ] + } + } + } + ], + connections: { + Webhook: { + main: [[{ node: 'Set 1', type: 'main', index: 0 }]] + } + }, + settings: {}, + tags: ['mcp-integration-test'] + }; + + const created = await client.createWorkflow(workflow); + context.trackWorkflow(created.id!); + + // Limit to 1 fix + const response = await handleAutofixWorkflow( + { + id: created.id, + applyFixes: false, + maxFixes: 1 + }, + repository, + mcpContext + ); + + expect(response.success).toBe(true); + const data = response.data as any; + + // Should have at most 1 fix + if (data.fixes) { + expect(data.fixes.length).toBeLessThanOrEqual(1); + } + }); + }); + + // ====================================================================== + // No Fixes Available + // ====================================================================== + + describe('No Fixes Available', () => { + it('should handle workflow with no fixable issues', async () => { + // Create valid workflow + const workflow = { + name: createTestWorkflowName('Autofix - No Issues'), + nodes: [ + { + id: 'webhook-1', + name: 'Webhook', + type: 'n8n-nodes-base.webhook', + typeVersion: 2, + position: [250, 300] as [number, number], + parameters: { + httpMethod: 'GET', + path: 'test-webhook' + } + } + ], + connections: {}, + settings: {}, + tags: ['mcp-integration-test'] + }; + + const created = await client.createWorkflow(workflow); + context.trackWorkflow(created.id!); + + const response = await handleAutofixWorkflow( + { + id: created.id, + applyFixes: false + }, + repository, + mcpContext + ); + + expect(response.success).toBe(true); + const data = response.data as any; + + expect(data.message).toContain('No automatic fixes available'); + expect(data.validationSummary).toBeDefined(); + }); + }); + + // ====================================================================== + // Error Handling + // ====================================================================== + + describe('Error Handling', () => { + it('should handle non-existent workflow ID', async () => { + const response = await handleAutofixWorkflow( + { + id: '99999999', + applyFixes: false + }, + repository, + mcpContext + ); + + expect(response.success).toBe(false); + expect(response.error).toBeDefined(); + }); + + it('should handle invalid fixTypes parameter', async () => { + const workflow = { + name: createTestWorkflowName('Autofix - Invalid Param'), + nodes: [ + { + id: 'webhook-1', + name: 'Webhook', + type: 'n8n-nodes-base.webhook', + typeVersion: 2, + position: [250, 300] as [number, number], + parameters: { + httpMethod: 'GET', + path: 'test' + } + } + ], + connections: {}, + settings: {}, + tags: ['mcp-integration-test'] + }; + + const created = await client.createWorkflow(workflow); + context.trackWorkflow(created.id!); + + const response = await handleAutofixWorkflow( + { + id: created.id, + applyFixes: false, + fixTypes: ['invalid-fix-type'] as any + }, + repository, + mcpContext + ); + + // Should either fail validation or ignore invalid type + expect(response.success).toBe(false); + }); + + it('should handle invalid confidence threshold', async () => { + const workflow = { + name: createTestWorkflowName('Autofix - Invalid Confidence'), + nodes: [ + { + id: 'webhook-1', + name: 'Webhook', + type: 'n8n-nodes-base.webhook', + typeVersion: 2, + position: [250, 300] as [number, number], + parameters: { + httpMethod: 'GET', + path: 'test' + } + } + ], + connections: {}, + settings: {}, + tags: ['mcp-integration-test'] + }; + + const created = await client.createWorkflow(workflow); + context.trackWorkflow(created.id!); + + const response = await handleAutofixWorkflow( + { + id: created.id, + applyFixes: false, + confidenceThreshold: 'invalid' as any + }, + repository, + mcpContext + ); + + expect(response.success).toBe(false); + }); + }); + + // ====================================================================== + // Response Format Verification + // ====================================================================== + + describe('Response Format', () => { + it('should return complete autofix response structure (preview)', async () => { + const workflow = { + name: createTestWorkflowName('Autofix - Response Format Preview'), + nodes: [ + { + id: 'webhook-1', + name: 'Webhook', + type: 'n8n-nodes-base.webhook', + typeVersion: 2, + position: [250, 300] as [number, number], + parameters: { + httpMethod: 'GET' + // Missing path to trigger fixes + } + } + ], + connections: {}, + settings: {}, + tags: ['mcp-integration-test'] + }; + + const created = await client.createWorkflow(workflow); + context.trackWorkflow(created.id!); + + const response = await handleAutofixWorkflow( + { + id: created.id, + applyFixes: false + }, + repository, + mcpContext + ); + + expect(response.success).toBe(true); + const data = response.data as any; + + // Verify required fields + expect(data).toHaveProperty('workflowId'); + expect(data).toHaveProperty('workflowName'); + + // Preview mode specific fields + if (data.fixesAvailable > 0) { + expect(data).toHaveProperty('preview'); + expect(data.preview).toBe(true); + expect(data).toHaveProperty('fixesAvailable'); + expect(data).toHaveProperty('fixes'); + expect(data).toHaveProperty('summary'); + expect(data).toHaveProperty('stats'); + expect(data).toHaveProperty('message'); + + // Verify fixes structure + expect(Array.isArray(data.fixes)).toBe(true); + if (data.fixes.length > 0) { + const fix = data.fixes[0]; + expect(fix).toHaveProperty('type'); + expect(fix).toHaveProperty('confidence'); + expect(fix).toHaveProperty('description'); + } + } + }); + + it('should return complete autofix response structure (apply)', async () => { + const workflow = { + name: createTestWorkflowName('Autofix - Response Format Apply'), + nodes: [ + { + id: 'webhook-1', + name: 'Webhook', + type: 'n8n-nodes-base.webhook', + typeVersion: 2, + position: [250, 300] as [number, number], + parameters: { + httpMethod: 'GET' + // Missing path + } + } + ], + connections: {}, + settings: {}, + tags: ['mcp-integration-test'] + }; + + const created = await client.createWorkflow(workflow); + context.trackWorkflow(created.id!); + + const response = await handleAutofixWorkflow( + { + id: created.id, + applyFixes: true + }, + repository, + mcpContext + ); + + expect(response.success).toBe(true); + const data = response.data as any; + + expect(data).toHaveProperty('workflowId'); + expect(data).toHaveProperty('workflowName'); + + // Apply mode specific fields + if (data.fixesApplied > 0) { + expect(data).toHaveProperty('fixesApplied'); + expect(data).toHaveProperty('fixes'); + expect(data).toHaveProperty('summary'); + expect(data).toHaveProperty('stats'); + expect(data).toHaveProperty('message'); + expect(data.preview).toBeUndefined(); + + // Verify types + expect(typeof data.fixesApplied).toBe('number'); + expect(Array.isArray(data.fixes)).toBe(true); + } + }); + }); +});