Skip to content

Improve integration test coverage for n8n API workflow updates #433

@czlonkowski

Description

@czlonkowski

Problem

Issue #431 (description field causing API errors) wasn't caught by our existing test suite. After analysis, we identified several critical gaps in our integration test coverage that allowed this bug to slip through.

Root Causes

1. Missing GET-then-UPDATE Round-Trip Pattern

Our integration tests cherry-pick fields instead of testing the common spread operator pattern:

What we test:

const current = await client.getWorkflow(id);
await updateWorkflow(id, {
  name: current.name,
  nodes: current.nodes,
  connections: current.connections
});

What we DON'T test (but users commonly do):

const current = await client.getWorkflow(id);
await updateWorkflow(id, {
  ...current,  // Spread includes ALL fields including description
  name: 'New Name'
});

2. n8n API Asymmetric Contract Not Tested

The n8n API has quirks we don't explicitly test:

  • GET returns description field ✅
  • PUT/PATCH rejects description field ❌
  • PUT/PATCH requires settings field ✅
  • But empty settings: {} is rejected ❌

3. Missing Minimal Payload Tests

No tests verify updates with absolute minimum required fields (name, nodes, connections only).

4. API Constraints Not Explicitly Validated

We test our cleaning logic but don't explicitly test that the API rejects/requires specific fields.

Proposed Solution

Add comprehensive integration tests for common real-world patterns:

Test Suite 1: GET-UPDATE Round Trip

describe('GET-UPDATE Round Trip', () => {
  it('should handle workflow returned from GET in UPDATE', async () => {
    const workflow = await client.getWorkflow(id);
    // Should clean description and other read-only fields
    await client.updateWorkflow(id, workflow);
  });
  
  it('should handle workflow with spread operator', async () => {
    const workflow = await client.getWorkflow(id);
    await client.updateWorkflow(id, {
      ...workflow,
      name: 'Updated Name'
    });
  });
  
  it('should handle partial updates with spread', async () => {
    const workflow = await client.getWorkflow(id);
    await client.updateWorkflow(id, {
      ...workflow,
      settings: {
        ...workflow.settings,
        timezone: 'UTC'
      }
    });
  });
});

Test Suite 2: n8n API Constraint Testing

describe('n8n API Constraints', () => {
  it('should handle description field (read-only in updates)', async () => {
    const workflow = await client.getWorkflow(id);
    // Verify description is present in GET
    expect(workflow.description).toBeDefined();
    
    // Update with description should work (auto-cleaned)
    await client.updateWorkflow(id, {
      ...workflow,
      description: 'New description'
    });
  });
  
  it('should provide settings when not explicitly provided', async () => {
    const workflow = await client.getWorkflow(id);
    // Update without settings should auto-add minimal defaults
    await client.updateWorkflow(id, {
      name: workflow.name,
      nodes: workflow.nodes,
      connections: workflow.connections
      // No settings provided
    });
  });
  
  it('should handle all read-only fields gracefully', async () => {
    const workflow = await client.getWorkflow(id);
    // Include all read-only fields - should be auto-cleaned
    await client.updateWorkflow(id, {
      ...workflow,
      createdAt: 'invalid',
      updatedAt: 'invalid',
      versionId: 'invalid',
      active: true,
      // All should be filtered out
    });
  });
});

Test Suite 3: Minimal Payload Testing

describe('Minimal Updates', () => {
  it('should update with only required fields', async () => {
    const current = await client.getWorkflow(id);
    await client.updateWorkflow(id, {
      name: current.name,
      nodes: current.nodes,
      connections: current.connections
      // Absolute minimum - settings added automatically
    });
  });
  
  it('should update with minimal changes', async () => {
    const current = await client.getWorkflow(id);
    await client.updateWorkflow(id, {
      name: 'New Name',
      nodes: current.nodes,
      connections: current.connections
    });
  });
});

Test Suite 4: Edge Cases

describe('Edge Cases', () => {
  it('should handle workflow with only filtered-out settings', async () => {
    await client.updateWorkflow(id, {
      name: 'Test',
      nodes: [...],
      connections: {},
      settings: {
        callerPolicy: 'value',  // Will be filtered out
        // Should add minimal defaults when all filtered
      }
    });
  });
  
  it('should preserve valid settings while filtering unsafe ones', async () => {
    await client.updateWorkflow(id, {
      name: 'Test',
      nodes: [...],
      connections: {},
      settings: {
        executionOrder: 'v1',  // Valid - keep
        callerPolicy: 'value',  // Invalid - filter
        timezone: 'UTC'  // Valid - keep
      }
    });
  });
});

Acceptance Criteria

  • Add GET-UPDATE round-trip tests (3 tests)
  • Add n8n API constraint tests (3 tests)
  • Add minimal payload tests (2 tests)
  • Add edge case tests (2 tests)
  • All new tests pass in CI
  • Document n8n API quirks in test comments
  • Update test documentation/README

Additional Notes

These tests would have caught:

  1. Issue Codex Update Partial Workflow repeated Failure #431 (description field error)
  2. The follow-up issue (missing settings requirement)
  3. Future API contract changes

Estimated effort: 2-3 hours

Related Issues

Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions