mirror of
https://github.com/czlonkowski/n8n-mcp.git
synced 2026-03-18 16:33:13 +00:00
fix: resolve multiple n8n_update_partial_workflow bugs (#635)
* fix: use correct MCP SDK API for server capabilities in test getServerVersion() returns Implementation (name/version only), not the full init result. Use client.getServerCapabilities() instead to access server capabilities, fixing the CI typecheck failure. Concieved by Romuald Członkowski - www.aiadvisors.pl/en Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: resolve multiple n8n_update_partial_workflow bugs (#592, #599, #610, #623, #624, #625, #629, #630, #633) Phase 1 - Data loss prevention: - Add missing unary operators (empty, notEmpty, exists, notExists) to sanitizer (#592) - Preserve positional empty arrays in connections during removeNode/cleanStale (#610) - Scope sanitization to modified nodes only, preventing unrelated node corruption - Add empty body {} to activate/deactivate POST calls to fix 415 errors (#633) Phase 2 - Error handling & response clarity: - Serialize Zod errors to readable "path: message" strings (#630) - Add saved:true/false field to all response paths (#625) - Improve updateNode error hint with correct structure example (#623) - Track removed node names for better removeConnection errors (#624) Phase 3 - Connection & type fixes: - Coerce sourceOutput/targetInput to String() consistently (#629) - Accept numeric sourceOutput/targetInput at Zod schema level via transform Phase 4 - Tag operations via dedicated API (#599): - Track tags as tagsToAdd/tagsToRemove instead of mutating workflow.tags - Orchestrate tag creation and association via listTags/createTag/updateWorkflowTags - Reconcile conflicting add/remove for same tag (last operation wins) - Tag failures produce warnings, not hard errors Conceived by Romuald Członkowski - www.aiadvisors.pl/en Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: add v2.37.0 changelog entry Conceived by Romuald Członkowski - www.aiadvisors.pl/en Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: resolve pre-existing integration test failures in CI - Create new MCP Server instance per connection in test helpers (SDK 1.27+ requires separate Protocol instance per connection) - Normalize database paths with path.resolve() in shared-database singleton to prevent path mismatch errors across test files - Add no-op catch handler to deferred initialization promise in server.ts to prevent unhandled rejection warnings - Properly call mcpServer.shutdown() in test helper close() to release shared database references Conceived by Romuald Członkowski - www.aiadvisors.pl/en Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
committed by
GitHub
parent
248f859c49
commit
9590f751d2
@@ -424,7 +424,7 @@ describe('WorkflowDiffEngine', () => {
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.errors![0].message).toContain('Missing required parameter \'updates\'');
|
||||
expect(result.errors![0].message).toContain('Example:');
|
||||
expect(result.errors![0].message).toContain('Correct structure:');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1898,16 +1898,15 @@ describe('WorkflowDiffEngine', () => {
|
||||
};
|
||||
|
||||
const result = await diffEngine.applyDiff(baseWorkflow, request);
|
||||
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.workflow!.tags).toContain('production');
|
||||
expect(result.workflow!.tags).toHaveLength(3);
|
||||
expect(result.tagsToAdd).toContain('production');
|
||||
});
|
||||
|
||||
it('should not add duplicate tags', async () => {
|
||||
const operation: AddTagOperation = {
|
||||
type: 'addTag',
|
||||
tag: 'test' // Already exists
|
||||
tag: 'test' // Already exists in workflow but tagsToAdd tracks it for API
|
||||
};
|
||||
|
||||
const request: WorkflowDiffRequest = {
|
||||
@@ -1916,9 +1915,10 @@ describe('WorkflowDiffEngine', () => {
|
||||
};
|
||||
|
||||
const result = await diffEngine.applyDiff(baseWorkflow, request);
|
||||
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.workflow!.tags).toHaveLength(2); // No change
|
||||
// Tags are now tracked for dedicated API call, not modified on workflow
|
||||
expect(result.tagsToAdd).toEqual(['test']);
|
||||
});
|
||||
|
||||
it('should create tags array if not exists', async () => {
|
||||
@@ -1935,10 +1935,9 @@ describe('WorkflowDiffEngine', () => {
|
||||
};
|
||||
|
||||
const result = await diffEngine.applyDiff(baseWorkflow, request);
|
||||
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.workflow!.tags).toBeDefined();
|
||||
expect(result.workflow!.tags).toEqual(['new-tag']);
|
||||
expect(result.tagsToAdd).toEqual(['new-tag']);
|
||||
});
|
||||
|
||||
it('should remove an existing tag', async () => {
|
||||
@@ -1953,10 +1952,9 @@ describe('WorkflowDiffEngine', () => {
|
||||
};
|
||||
|
||||
const result = await diffEngine.applyDiff(baseWorkflow, request);
|
||||
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.workflow!.tags).not.toContain('test');
|
||||
expect(result.workflow!.tags).toHaveLength(1);
|
||||
expect(result.tagsToRemove).toContain('test');
|
||||
});
|
||||
|
||||
it('should handle removing non-existent tag gracefully', async () => {
|
||||
@@ -1971,9 +1969,11 @@ describe('WorkflowDiffEngine', () => {
|
||||
};
|
||||
|
||||
const result = await diffEngine.applyDiff(baseWorkflow, request);
|
||||
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.workflow!.tags).toHaveLength(2); // No change
|
||||
expect(result.tagsToRemove).toEqual(['non-existent']);
|
||||
// workflow.tags unchanged since tags are now handled via dedicated API
|
||||
expect(result.workflow!.tags).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2509,7 +2509,7 @@ describe('WorkflowDiffEngine', () => {
|
||||
expect(result.failed).toEqual([1]); // Operation 1 failed
|
||||
expect(result.errors).toHaveLength(1);
|
||||
expect(result.workflow.name).toBe('New Workflow Name');
|
||||
expect(result.workflow.tags).toContain('production');
|
||||
expect(result.tagsToAdd).toContain('production');
|
||||
});
|
||||
|
||||
it('should return success false if all operations fail in continueOnError mode', async () => {
|
||||
@@ -3356,7 +3356,7 @@ describe('WorkflowDiffEngine', () => {
|
||||
expect(result.failed).toContain(1); // replaceConnections with invalid node
|
||||
expect(result.applied).toContain(2); // removeConnection with ignoreErrors
|
||||
expect(result.applied).toContain(3); // addTag
|
||||
expect(result.workflow.tags).toContain('final-tag');
|
||||
expect(result.tagsToAdd).toContain('final-tag');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4610,7 +4610,7 @@ describe('WorkflowDiffEngine', () => {
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.operationsApplied).toBe(3);
|
||||
expect(result.workflow!.name).toBe('Updated Workflow Name');
|
||||
expect(result.workflow!.tags).toContain('production');
|
||||
expect(result.tagsToAdd).toContain('production');
|
||||
expect(result.shouldActivate).toBe(true);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user