Claude
Skills
Sign in
Back

n8n-integration-testing-patterns

Included with Lifetime
$97 forever

API contract testing, authentication flows, rate limit handling, and error scenario coverage for n8n integrations with external services. Use when testing n8n node integrations.

n8n-testingn8nintegrationapiauthenticationoauthrate-limitingtestingscripts

What this skill does


# n8n Integration Testing Patterns

<default_to_action>
When testing n8n integrations:
1. VERIFY connectivity and authentication
2. TEST all configured operations
3. VALIDATE API response handling
4. CHECK rate limit behavior
5. CONFIRM error handling works

**Quick Integration Checklist:**
- Credentials valid and not expired
- API permissions sufficient for operations
- Rate limits understood and respected
- Error responses properly handled
- Data formats match API expectations

**Critical Success Factors:**
- Test in isolation before workflow integration
- Verify OAuth token refresh works
- Check API version compatibility
- Monitor rate limit headers
</default_to_action>

## Quick Reference Card

### Common n8n Integrations

| Category | Services | Auth Type |
|----------|----------|-----------|
| **Communication** | Slack, Teams, Discord | OAuth2, Webhook |
| **Data Storage** | Google Sheets, Airtable | OAuth2, API Key |
| **CRM** | Salesforce, HubSpot | OAuth2 |
| **Dev Tools** | GitHub, Jira, Linear | OAuth2, API Key |
| **Marketing** | Mailchimp, SendGrid | API Key |

### Authentication Types

| Type | Setup | Refresh |
|------|-------|---------|
| **OAuth2** | User authorization flow | Automatic token refresh |
| **API Key** | Manual key entry | Manual rotation |
| **Basic Auth** | Username/password | No refresh needed |
| **Header Auth** | Custom header | Manual rotation |

---

## Connectivity Testing

```typescript
// Test integration connectivity
async function testIntegrationConnectivity(nodeName: string): Promise<ConnectivityResult> {
  const node = await getNodeConfig(nodeName);

  // Check credential exists
  if (!node.credentials) {
    return { connected: false, error: 'No credentials configured' };
  }

  // Test based on integration type
  switch (getIntegrationType(node.type)) {
    case 'slack':
      return await testSlackConnectivity(node.credentials);
    case 'google-sheets':
      return await testGoogleSheetsConnectivity(node.credentials);
    case 'jira':
      return await testJiraConnectivity(node.credentials);
    case 'github':
      return await testGitHubConnectivity(node.credentials);
    default:
      return await testGenericAPIConnectivity(node);
  }
}

// Slack connectivity test
async function testSlackConnectivity(credentials: any): Promise<ConnectivityResult> {
  try {
    const response = await fetch('https://slack.com/api/auth.test', {
      headers: { 'Authorization': `Bearer ${credentials.accessToken}` }
    });
    const data = await response.json();

    return {
      connected: data.ok,
      workspace: data.team,
      user: data.user,
      scopes: data.response_metadata?.scopes || []
    };
  } catch (error) {
    return { connected: false, error: error.message };
  }
}

// Google Sheets connectivity test
async function testGoogleSheetsConnectivity(credentials: any): Promise<ConnectivityResult> {
  try {
    const response = await fetch('https://www.googleapis.com/drive/v3/about?fields=user', {
      headers: { 'Authorization': `Bearer ${credentials.accessToken}` }
    });

    if (response.status === 401) {
      // Try refresh
      const refreshed = await refreshOAuthToken(credentials);
      if (refreshed) {
        return testGoogleSheetsConnectivity({ ...credentials, accessToken: refreshed });
      }
      return { connected: false, error: 'Token expired, refresh failed' };
    }

    const data = await response.json();
    return { connected: true, user: data.user };
  } catch (error) {
    return { connected: false, error: error.message };
  }
}
```

---

## API Operation Testing

```typescript
// Test integration operations
async function testIntegrationOperations(nodeName: string): Promise<OperationResult[]> {
  const node = await getNodeConfig(nodeName);
  const operations = getNodeOperations(node.type);
  const results: OperationResult[] = [];

  for (const operation of operations) {
    const testData = generateTestData(node.type, operation);

    try {
      const startTime = Date.now();
      const response = await executeOperation(node, operation, testData);

      results.push({
        operation,
        success: true,
        responseTime: Date.now() - startTime,
        responseStatus: response.status,
        dataValid: validateResponseData(response.data, operation)
      });
    } catch (error) {
      results.push({
        operation,
        success: false,
        error: error.message,
        errorType: classifyError(error)
      });
    }
  }

  return results;
}

// Generate test data for operations
function generateTestData(nodeType: string, operation: string): any {
  const testDataMap = {
    'slack': {
      'postMessage': {
        channel: 'C123456',
        text: 'Test message from n8n integration test'
      },
      'uploadFile': {
        channels: 'C123456',
        content: 'Test file content',
        filename: 'test.txt'
      }
    },
    'google-sheets': {
      'appendData': {
        spreadsheetId: 'test-spreadsheet-id',
        range: 'Sheet1!A:Z',
        values: [['Test', 'Data', new Date().toISOString()]]
      },
      'readRows': {
        spreadsheetId: 'test-spreadsheet-id',
        range: 'Sheet1!A1:Z10'
      }
    },
    'jira': {
      'createIssue': {
        project: 'TEST',
        issueType: 'Task',
        summary: 'Test issue from n8n',
        description: 'Created by integration test'
      },
      'updateIssue': {
        issueKey: 'TEST-1',
        fields: { summary: 'Updated by n8n test' }
      }
    }
  };

  return testDataMap[nodeType]?.[operation] || {};
}
```

---

## Authentication Testing

### OAuth2 Flow Testing

```typescript
// Test OAuth2 authentication
async function testOAuth2Authentication(credentials: any): Promise<OAuth2Result> {
  const result: OAuth2Result = {
    tokenValid: false,
    refreshWorking: false,
    scopes: [],
    expiresIn: 0
  };

  // Test current token
  const tokenTest = await testAccessToken(credentials.accessToken);
  result.tokenValid = tokenTest.valid;
  result.scopes = tokenTest.scopes;

  // Check expiration
  if (credentials.expiresAt) {
    result.expiresIn = new Date(credentials.expiresAt).getTime() - Date.now();
    result.expiresSoon = result.expiresIn < 3600000; // Less than 1 hour
  }

  // Test refresh token
  if (credentials.refreshToken) {
    try {
      const newToken = await refreshOAuthToken(credentials);
      result.refreshWorking = !!newToken;
    } catch (error) {
      result.refreshError = error.message;
    }
  }

  return result;
}

// Test required scopes
async function testRequiredScopes(credentials: any, requiredScopes: string[]): Promise<ScopeResult> {
  const currentScopes = await getTokenScopes(credentials.accessToken);
  const missingScopes = requiredScopes.filter(s => !currentScopes.includes(s));

  return {
    hasAllScopes: missingScopes.length === 0,
    currentScopes,
    missingScopes,
    recommendation: missingScopes.length > 0
      ? `Re-authorize with scopes: ${missingScopes.join(', ')}`
      : null
  };
}
```

### API Key Testing

```typescript
// Test API key validity
async function testAPIKey(integration: string, apiKey: string): Promise<APIKeyResult> {
  const endpoints = {
    'sendgrid': 'https://api.sendgrid.com/v3/user/profile',
    'mailchimp': 'https://us1.api.mailchimp.com/3.0/ping',
    'airtable': 'https://api.airtable.com/v0/meta/whoami'
  };

  const endpoint = endpoints[integration];
  if (!endpoint) {
    return { valid: false, error: 'Unknown integration' };
  }

  try {
    const response = await fetch(endpoint, {
      headers: { 'Authorization': `Bearer ${apiKey}` }
    });

    return {
      valid: response.status === 200,
      status: response.status,
      rateLimit: extractRateLimitInfo(response.headers)
    };
  } catch (error) {
    return { valid: false, error: error.message };
  }
}
```

---

## Rate Limit Testing

```typescript
// Test rate limit handling
async function testRateLimits(nodeName: string, requestC

Related in n8n-testing