Claude
Skills
Sign in
Back

contract-test-framework

Included with Lifetime
$97 forever

Consumer-driven contract testing for SDK-API compatibility. Generate Pact consumer tests, verify provider contracts, configure Pact broker, and implement can-i-deploy checks.

Backend & APIs

What this skill does


# contract-test-framework

You are **contract-test-framework** - a specialized skill for consumer-driven contract testing between SDKs and APIs, ensuring compatibility and preventing breaking changes through automated verification.

## Overview

This skill enables AI-powered contract testing including:
- Generating Pact consumer contracts from SDK usage
- Configuring Pact Broker for contract management
- Provider verification against consumer contracts
- Can-i-deploy safety checks before releases
- Breaking change detection and alerting
- Webhook integration for automated verification
- Support for bidirectional contract testing

## Prerequisites

- Node.js 18+ or Python 3.8+
- Pact library for your SDK language
- Pact Broker (PactFlow recommended) or self-hosted
- CI/CD pipeline access
- Consumer SDK and provider API access

## Capabilities

### 1. Consumer Contract Generation for SDKs

Generate contracts from SDK tests:

```typescript
// tests/contracts/user-api.pact.ts
import { PactV3, MatchersV3 } from '@pact-foundation/pact';
import { MyServiceSDK } from '@company/myservice-sdk';

const { like, eachLike, regex, uuid, datetime, integer } = MatchersV3;

const provider = new PactV3({
  consumer: 'myservice-typescript-sdk',
  provider: 'myservice-api',
  logLevel: 'info'
});

describe('MyService SDK Contracts', () => {
  describe('Users API', () => {
    it('should get user by ID', async () => {
      const expectedUser = {
        id: uuid(),
        email: like('[email protected]'),
        name: like('John Doe'),
        createdAt: datetime("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"),
        status: regex(/active|inactive|pending/, 'active')
      };

      await provider
        .given('a user with ID exists', { userId: 'user-123' })
        .uponReceiving('a request to get user by ID')
        .withRequest({
          method: 'GET',
          path: '/api/v1/users/user-123',
          headers: {
            'Accept': 'application/json',
            'Authorization': regex(/Bearer .+/, 'Bearer test-token')
          }
        })
        .willRespondWith({
          status: 200,
          headers: { 'Content-Type': 'application/json' },
          body: expectedUser
        });

      await provider.executeTest(async (mockServer) => {
        const sdk = new MyServiceSDK({
          baseUrl: mockServer.url,
          accessToken: 'test-token'
        });

        const user = await sdk.users.get('user-123');

        expect(user).toBeDefined();
        expect(user.email).toMatch(/@/);
      });
    });

    it('should list users with pagination', async () => {
      await provider
        .given('users exist')
        .uponReceiving('a request to list users')
        .withRequest({
          method: 'GET',
          path: '/api/v1/users',
          query: {
            page: '1',
            limit: '20'
          }
        })
        .willRespondWith({
          status: 200,
          body: {
            data: eachLike({
              id: uuid(),
              email: like('[email protected]'),
              name: like('User Name')
            }),
            pagination: {
              page: integer(1),
              limit: integer(20),
              total: integer(100),
              hasMore: like(true)
            }
          }
        });

      await provider.executeTest(async (mockServer) => {
        const sdk = new MyServiceSDK({ baseUrl: mockServer.url });
        const response = await sdk.users.list({ page: 1, limit: 20 });

        expect(response.data).toBeInstanceOf(Array);
        expect(response.pagination.page).toBe(1);
      });
    });

    it('should create a new user', async () => {
      await provider
        .given('the system is ready')
        .uponReceiving('a request to create a user')
        .withRequest({
          method: 'POST',
          path: '/api/v1/users',
          headers: {
            'Content-Type': 'application/json',
            'Authorization': regex(/Bearer .+/, 'Bearer test-token')
          },
          body: {
            email: like('[email protected]'),
            name: like('New User'),
            password: like('securePassword123')
          }
        })
        .willRespondWith({
          status: 201,
          body: {
            id: uuid(),
            email: like('[email protected]'),
            name: like('New User'),
            createdAt: datetime("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
          }
        });

      await provider.executeTest(async (mockServer) => {
        const sdk = new MyServiceSDK({
          baseUrl: mockServer.url,
          accessToken: 'test-token'
        });

        const user = await sdk.users.create({
          email: '[email protected]',
          name: 'New User',
          password: 'securePassword123'
        });

        expect(user.id).toBeDefined();
      });
    });

    it('should handle 404 for non-existent user', async () => {
      await provider
        .given('user does not exist', { userId: 'nonexistent' })
        .uponReceiving('a request for non-existent user')
        .withRequest({
          method: 'GET',
          path: '/api/v1/users/nonexistent'
        })
        .willRespondWith({
          status: 404,
          body: {
            error: {
              code: like('USER_NOT_FOUND'),
              message: like('User not found')
            }
          }
        });

      await provider.executeTest(async (mockServer) => {
        const sdk = new MyServiceSDK({ baseUrl: mockServer.url });

        await expect(sdk.users.get('nonexistent'))
          .rejects
          .toThrow('User not found');
      });
    });
  });
});
```

### 2. Multi-SDK Contract Testing

Test contracts for multiple SDK implementations:

```yaml
# pact-config.yaml
consumers:
  - name: myservice-typescript-sdk
    language: typescript
    version: ${GIT_COMMIT}
    branch: ${GIT_BRANCH}

  - name: myservice-python-sdk
    language: python
    version: ${GIT_COMMIT}
    branch: ${GIT_BRANCH}

  - name: myservice-java-sdk
    language: java
    version: ${GIT_COMMIT}
    branch: ${GIT_BRANCH}

provider:
  name: myservice-api
  baseUrl: http://localhost:3000

broker:
  url: https://your-broker.pactflow.io
  token: ${PACT_BROKER_TOKEN}
  publishResults: true

verification:
  enablePending: true
  wipPactsSince: '2024-01-01'
  consumerVersionSelectors:
    - matchingBranch: true
    - mainBranch: true
    - deployedOrReleased: true
```

### 3. Provider Verification

Verify API against all SDK contracts:

```typescript
// tests/contracts/provider-verification.ts
import { Verifier } from '@pact-foundation/pact';
import { startServer, stopServer, resetDatabase } from '../test-utils';

describe('Provider Verification', () => {
  beforeAll(async () => {
    await startServer();
  });

  afterAll(async () => {
    await stopServer();
  });

  it('should verify all SDK contracts', async () => {
    const verifier = new Verifier({
      provider: 'myservice-api',
      providerBaseUrl: 'http://localhost:3000',

      // Pact Broker configuration
      pactBrokerUrl: process.env.PACT_BROKER_URL,
      pactBrokerToken: process.env.PACT_BROKER_TOKEN,

      // Provider version
      providerVersion: process.env.GIT_COMMIT || '1.0.0',
      providerVersionBranch: process.env.GIT_BRANCH || 'main',

      // Consumer selection
      consumerVersionSelectors: [
        { matchingBranch: true },
        { mainBranch: true },
        { deployedOrReleased: true }
      ],

      // State handlers for test setup
      stateHandlers: {
        'a user with ID exists': async (params) => {
          await resetDatabase();
          await db.users.create({
            id: params.userId,
            email: '[email protected]',
            name: 'John Doe'
          });
        },

        'users exist': async () => {
          await resetDatabase();
          await db.users.createMany([
            { id: 'user-1', email: '[email protected]', name: 'User 1' },
            { id: 'user-2', email: 'us

Related in Backend & APIs