Marketplace

provider-verification

Provider verification workflow for contract testing

allowed_tools: Read, Glob, Grep, Write, Edit

$ Installer

git clone https://github.com/melodic-software/claude-code-plugins /tmp/claude-code-plugins && cp -r /tmp/claude-code-plugins/plugins/contract-testing/skills/provider-verification ~/.claude/skills/claude-code-plugins

// tip: Run this command in your terminal to install the skill


name: provider-verification description: Provider verification workflow for contract testing allowed-tools: Read, Glob, Grep, Write, Edit

Provider Verification Skill

When to Use This Skill

Use this skill when:

  • Provider Verification tasks - Working on provider verification workflow for contract testing
  • Planning or design - Need guidance on Provider Verification approaches
  • Best practices - Want to follow established patterns and standards

Overview

Implement provider-side verification of consumer contracts using Pact.

MANDATORY: Documentation-First Approach

Before implementing provider verification:

  1. Invoke docs-management skill for verification patterns
  2. Verify Pact provider patterns via MCP servers (context7, perplexity)
  3. Base guidance on Pact documentation and verification best practices

Provider Verification Overview

PROVIDER VERIFICATION PROCESS:

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    PROVIDER VERIFICATION                        โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                                 โ”‚
โ”‚  1. FETCH CONTRACTS                                             โ”‚
โ”‚     โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”           โ”‚
โ”‚     โ”‚ Get contracts from Pact Broker               โ”‚           โ”‚
โ”‚     โ”‚ - For specific consumers                     โ”‚           โ”‚
โ”‚     โ”‚ - For specific branches/environments         โ”‚           โ”‚
โ”‚     โ”‚ - Using consumer version selectors           โ”‚           โ”‚
โ”‚     โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜           โ”‚
โ”‚                                                                 โ”‚
โ”‚  2. SETUP PROVIDER STATES                                       โ”‚
โ”‚     โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”           โ”‚
โ”‚     โ”‚ For each interaction:                        โ”‚           โ”‚
โ”‚     โ”‚ - Parse provider state from contract         โ”‚           โ”‚
โ”‚     โ”‚ - Execute state handler                      โ”‚           โ”‚
โ”‚     โ”‚ - Seed required data                         โ”‚           โ”‚
โ”‚     โ”‚ - Configure dependencies                     โ”‚           โ”‚
โ”‚     โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜           โ”‚
โ”‚                                                                 โ”‚
โ”‚  3. REPLAY INTERACTIONS                                         โ”‚
โ”‚     โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”           โ”‚
โ”‚     โ”‚ For each interaction:                        โ”‚           โ”‚
โ”‚     โ”‚ - Send request to real provider              โ”‚           โ”‚
โ”‚     โ”‚ - Capture actual response                    โ”‚           โ”‚
โ”‚     โ”‚ - Compare against expected                   โ”‚           โ”‚
โ”‚     โ”‚ - Apply matching rules                       โ”‚           โ”‚
โ”‚     โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜           โ”‚
โ”‚                                                                 โ”‚
โ”‚  4. PUBLISH RESULTS                                             โ”‚
โ”‚     โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”           โ”‚
โ”‚     โ”‚ Report verification results to Pact Broker   โ”‚           โ”‚
โ”‚     โ”‚ - Tag with provider version                  โ”‚           โ”‚
โ”‚     โ”‚ - Include branch/environment info            โ”‚           โ”‚
โ”‚     โ”‚ - Enable can-i-deploy checks                 โ”‚           โ”‚
โ”‚     โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜           โ”‚
โ”‚                                                                 โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Provider Verification Test (C#)

// Provider verification test
// File: OrdersApi.Provider.Tests/OrdersApiProviderTests.cs

using PactNet;
using PactNet.Verifier;
using Microsoft.AspNetCore.Mvc.Testing;

public class OrdersApiProviderTests : IClassFixture<WebApplicationFactory<Program>>
{
    private readonly WebApplicationFactory<Program> _factory;
    private readonly ITestOutputHelper _output;

    public OrdersApiProviderTests(
        WebApplicationFactory<Program> factory,
        ITestOutputHelper output)
    {
        _factory = factory;
        _output = output;
    }

    [Fact]
    public void VerifyPacts_WithAllConsumers()
    {
        // Arrange
        var config = new PactVerifierConfig
        {
            LogLevel = PactLogLevel.Information,
            Outputters = new[] { new XUnitOutput(_output) }
        };

        // Start the provider API
        using var server = _factory.CreateDefaultClient();
        var providerUri = _factory.Server.BaseAddress;

        // Act & Assert
        using var verifier = new PactVerifier("OrdersApi", config);

        verifier
            .WithHttpEndpoint(providerUri)
            .WithProviderStateUrl(new Uri(providerUri, "/provider-states"))
            .WithPactBrokerSource(new Uri("https://pact-broker.example.com"), options =>
            {
                options.TokenAuthentication("your-token");
                options.ConsumerVersionSelectors(
                    new ConsumerVersionSelector
                    {
                        MainBranch = true
                    },
                    new ConsumerVersionSelector
                    {
                        DeployedOrReleased = true
                    }
                );
                options.PublishVerificationResults(
                    providerVersion: Environment.GetEnvironmentVariable("GIT_SHA") ?? "local",
                    providerBranch: Environment.GetEnvironmentVariable("GIT_BRANCH"));
            })
            .Verify();
    }
}

Provider State Handlers

// Provider state endpoint
// File: OrdersApi/Controllers/ProviderStatesController.cs

[ApiController]
[Route("provider-states")]
public class ProviderStatesController : ControllerBase
{
    private readonly OrdersDbContext _context;
    private readonly IServiceProvider _serviceProvider;

    public ProviderStatesController(
        OrdersDbContext context,
        IServiceProvider serviceProvider)
    {
        _context = context;
        _serviceProvider = serviceProvider;
    }

    [HttpPost]
    public async Task<IActionResult> SetupState([FromBody] ProviderStateRequest request)
    {
        // Clear existing test data
        await CleanupTestData();

        // Handle the specific state
        await HandleState(request.State, request.Params);

        return Ok();
    }

    private async Task HandleState(string state, Dictionary<string, object>? parameters)
    {
        switch (state)
        {
            case "an order with id 123 exists":
                await SeedOrder("123", parameters);
                break;

            case "no order with id non-existent exists":
                // No setup needed - just ensure it doesn't exist
                break;

            case "customer customer-789 exists":
                await SeedCustomer("customer-789", parameters);
                break;

            case "an order was created":
                // For message pacts - seed the event data
                await SeedOrderCreatedEvent(parameters);
                break;

            case "the system has orders matching search criteria":
                await SeedMultipleOrders(parameters);
                break;

            default:
                throw new NotSupportedException($"Unknown provider state: {state}");
        }
    }

    private async Task SeedOrder(string orderId, Dictionary<string, object>? parameters)
    {
        var order = new Order
        {
            Id = orderId,
            CustomerId = GetParam(parameters, "customerId", "customer-456"),
            Status = GetParam(parameters, "status", "Pending"),
            Items = new List<OrderItem>
            {
                new OrderItem
                {
                    ProductId = "prod-1",
                    Quantity = 1,
                    Price = 99.99m
                }
            },
            Total = 99.99m,
            CreatedAt = DateTime.UtcNow
        };

        _context.Orders.Add(order);
        await _context.SaveChangesAsync();
    }

    private async Task SeedCustomer(string customerId, Dictionary<string, object>? parameters)
    {
        var customer = new Customer
        {
            Id = customerId,
            Name = GetParam(parameters, "name", "Test Customer"),
            Email = GetParam(parameters, "email", "test@example.com")
        };

        _context.Customers.Add(customer);
        await _context.SaveChangesAsync();
    }

    private async Task CleanupTestData()
    {
        // Remove test data to ensure clean state
        _context.Orders.RemoveRange(_context.Orders);
        _context.Customers.RemoveRange(_context.Customers);
        await _context.SaveChangesAsync();
    }

    private static T GetParam<T>(Dictionary<string, object>? parameters, string key, T defaultValue)
    {
        if (parameters == null || !parameters.TryGetValue(key, out var value))
            return defaultValue;

        return (T)Convert.ChangeType(value, typeof(T));
    }
}

public class ProviderStateRequest
{
    public string State { get; set; } = string.Empty;
    public Dictionary<string, object>? Params { get; set; }
}

Consumer Version Selectors

CONSUMER VERSION SELECTORS:

Purpose: Specify which consumer pacts to verify

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Selector               โ”‚ Meaning                                โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ MainBranch = true      โ”‚ Latest from main/master branch         โ”‚
โ”‚ DeployedOrReleased     โ”‚ Currently deployed versions            โ”‚
โ”‚ Branch = "feature-x"   โ”‚ Specific branch                        โ”‚
โ”‚ Consumer = "OrdersUI"  โ”‚ Specific consumer only                 โ”‚
โ”‚ MatchingBranch = true  โ”‚ Same branch as provider                โ”‚
โ”‚ Latest = true          โ”‚ Latest version (any branch)            โ”‚
โ”‚ Tag = "production"     โ”‚ Tagged with specific tag               โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

COMMON PATTERNS:

CI Pipeline (feature branch):
- MainBranch = true (always verify against main)
- MatchingBranch = true (verify against same feature branch)
- DeployedOrReleased = true (verify against production)

Release Pipeline:
- DeployedOrReleased = true (all deployed versions)
- MainBranch = true (latest main)

Development:
- Latest = true, Consumer = "OrdersUI" (specific consumer)

Provider State Patterns

STATE HANDLER PATTERNS:

1. DATA SEEDING
   - Create required entities in database
   - Set up relationships
   - Initialize with specific values

2. FEATURE FLAGS
   - Enable/disable features
   - Configure behavior modes

3. EXTERNAL SERVICE MOCKS
   - Configure mock responses
   - Simulate external dependencies

4. TIME MANIPULATION
   - Set system time for date-sensitive tests
   - Configure clock providers

5. AUTHENTICATION
   - Set up test users
   - Configure permissions

BEST PRACTICES:

DO:
โœ“ Isolate test data (don't affect real data)
โœ“ Clean up between states
โœ“ Support parameterized states
โœ“ Keep states deterministic
โœ“ Document state requirements

DON'T:
โœ— Share state between tests
โœ— Rely on existing data
โœ— Use real external services
โœ— Create side effects
โœ— Make states order-dependent

Provider State Test Isolation

// Using test containers for isolation
// File: OrdersApi.Provider.Tests/IsolatedProviderTests.cs

public class IsolatedProviderTests : IAsyncLifetime
{
    private readonly PostgreSqlContainer _postgres;
    private WebApplicationFactory<Program> _factory = null!;

    public IsolatedProviderTests()
    {
        _postgres = new PostgreSqlBuilder()
            .WithImage("postgres:16-alpine")
            .Build();
    }

    public async Task InitializeAsync()
    {
        await _postgres.StartAsync();

        _factory = new WebApplicationFactory<Program>()
            .WithWebHostBuilder(builder =>
            {
                builder.ConfigureServices(services =>
                {
                    // Replace database with test container
                    services.RemoveAll<DbContextOptions<OrdersDbContext>>();
                    services.AddDbContext<OrdersDbContext>(options =>
                        options.UseNpgsql(_postgres.GetConnectionString()));
                });
            });

        // Apply migrations
        using var scope = _factory.Services.CreateScope();
        var context = scope.ServiceProvider.GetRequiredService<OrdersDbContext>();
        await context.Database.MigrateAsync();
    }

    public async Task DisposeAsync()
    {
        await _factory.DisposeAsync();
        await _postgres.DisposeAsync();
    }

    [Fact]
    public void VerifyPacts()
    {
        // Test implementation using isolated database
    }
}

Verification Results

VERIFICATION OUTPUT:

Verifying a pact between OrdersClient and OrdersApi
  Given an order with id 123 exists
    a request for an existing order
      with GET /orders/123
        returns a response which
          has status code 200 โœ“
          has a matching body โœ“
          includes headers โœ“

  Given no order with id non-existent exists
    a request for a non-existent order
      with GET /orders/non-existent
        returns a response which
          has status code 404 โœ“
          has a matching body โœ“

  Given customer customer-789 exists
    a request to create a new order
      with POST /orders
        returns a response which
          has status code 201 โœ“
          has a matching body โœ“
          includes headers โœ“

3 interactions, 0 failures

FAILURE EXAMPLE:

  Given an order with id 123 exists
    a request for an existing order
      with GET /orders/123
        returns a response which
          has status code 200 โœ“
          has a matching body
            $.status -> Expected "Pending" but got "Processing" โœ—

1 interaction, 1 failure

CI/CD Integration

# GitHub Actions example
# .github/workflows/provider-verification.yml

name: Provider Verification

on:
  push:
    branches: [main, 'feature/**']
  workflow_dispatch:
    inputs:
      consumer:
        description: 'Specific consumer to verify'
        required: false

jobs:
  verify:
    runs-on: ubuntu-latest

    services:
      postgres:
        image: postgres:16
        env:
          POSTGRES_PASSWORD: test
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    steps:
      - uses: actions/checkout@v4

      - name: Setup .NET
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: '10.0.x'

      - name: Restore
        run: dotnet restore

      - name: Build
        run: dotnet build --no-restore

      - name: Verify Pacts
        env:
          GIT_SHA: ${{ github.sha }}
          GIT_BRANCH: ${{ github.ref_name }}
          PACT_BROKER_BASE_URL: ${{ secrets.PACT_BROKER_URL }}
          PACT_BROKER_TOKEN: ${{ secrets.PACT_BROKER_TOKEN }}
        run: dotnet test --filter "Category=Pact"

      - name: Can I Deploy?
        run: |
          pact-broker can-i-deploy \
            --pacticipant OrdersApi \
            --version ${{ github.sha }} \
            --to-environment production

Troubleshooting

COMMON VERIFICATION FAILURES:

1. STATE NOT FOUND
   Error: Unknown provider state: "order exists"
   Fix: Implement the state handler or check state name spelling

2. DATA NOT SEEDED
   Error: 404 when expecting 200
   Fix: Verify state handler seeds data correctly

3. RESPONSE MISMATCH
   Error: Expected "Pending" but got "Processing"
   Fix: Check state sets correct values, or use matchers in contract

4. MISSING FIELDS
   Error: $.newField was not found
   Fix: Add field to response or update contract to use optional matcher

5. STATE CLEANUP ISSUES
   Error: Duplicate key violation
   Fix: Ensure cleanup runs before each state setup

DEBUGGING TIPS:
โ€ข Enable verbose logging in verifier
โ€ข Log state handler execution
โ€ข Check database state after setup
โ€ข Verify provider URL is correct
โ€ข Ensure authentication is configured

Assessment Template

# Provider Verification Assessment: [Provider Name]

## Provider Profile
- **Provider:** [Name]
- **Consumers:** [List]
- **Test Environment:** [Local/CI/Isolated]

## State Handlers

| State | Handler | Data Seeded |
|-------|---------|-------------|
| [State] | [Method] | [What data] |

## Verification Configuration

- **Consumer Selectors:** [MainBranch, DeployedOrReleased, etc.]
- **Publish Results:** [Yes/No]
- **Provider Version:** [Source]
- **Branch Tracking:** [Yes/No]

## Test Isolation

- [ ] Database isolated per test run
- [ ] External services mocked
- [ ] State cleanup between interactions
- [ ] No shared mutable state

## CI/CD Integration

- [ ] Verification in pipeline
- [ ] Results published to broker
- [ ] Can-I-Deploy gate configured
- [ ] Branch protection rules set

## Issues and Gaps

| Issue | Severity | Resolution |
|-------|----------|------------|
| [Issue] | [H/M/L] | [Plan] |

Workflow

When implementing provider verification:

  1. Setup Verification Tests: Create provider test project
  2. Implement State Handlers: Handle all required states
  3. Configure Version Selectors: Choose which pacts to verify
  4. Ensure Test Isolation: Isolated database, mocked services
  5. Integrate with CI/CD: Automate verification
  6. Publish Results: Enable can-i-deploy checks

References

For detailed guidance:


Last Updated: 2025-12-26