# WireMock Runner Hosting

This guide provides step-by-step instructions for configuring WireMock Runner hosting in your Aspire AppHost. WireMock Runner orchestrates multiple WireMock mock services as a single managed resource, allowing you to pull mock definitions from WireMock Cloud using API tokens.

!!! warning "Experimental Feature"

    WireMock Runner APIs are marked as experimental (`SAIFMOCK001`). See [SAIFMOCK001 diagnostic](../../../reference/diagnostics/SAIFMOCK001.md) for suppression options.

[TOC]

## 📚 Useful Resources

- [WireMock Documentation](https://docs.wiremock.io)
- [WireMock Cloud](https://app.wiremock.cloud)
- [WireMock CLI - Recording Mocks](wiremock-cli-record.md)
- [WireMock CLI Hosting](host-wiremock-cli.md)

## 📋 Prerequisites

Before you begin, ensure you have:

- ✅ .NET 10.0 SDK installed
- ✅ Docker Desktop running
- ✅ Node.js and npm installed (required for the provision-and-pull phase; used to install the WireMock CLI automatically)
- ✅ An API project with 1 or more external API dependencies you wish to mock
- ✅ WireMock Cloud account and API token
- ✅ WireMock Cloud project(s) with mocks defined (or use auto-provisioning)

### 📁 Projects

- **AppHost Project**: Contains logic for setting up your application and dependencies

---

## 🛠️ WireMock Runner - Aspire Hosting Extensions

### Overview

The WireMock Runner namespace (`SAIF.Platform.Aspire.Hosting.WiremockRunner`) provides Aspire hosting extensions for orchestrating multiple WireMock mock services as a single managed resource. This enables you to:

- Manage multiple mock services through a single runner resource
- Pull mock definitions from WireMock Cloud using API tokens
- Auto-provision new mock APIs in WireMock Cloud with seed stubs
- Integrate with Aspire's service discovery
- Organize mock configurations in a dedicated directory
- Iterate on mock stubs without restarting the entire AppHost

> For a detailed comparison with WireMock CLI and other approaches, see [Comparison with Other Mocking Approaches](#comparison-with-other-mocking-approaches).

---

## 🚀 Step-by-Step Implementation

### Step 1: Add Package Reference

Add a reference to the `SAIF.Platform.Aspire.Hosting` NuGet package in your AppHost project:

```xml
<ItemGroup>
  <PackageReference Include="SAIF.Platform.Aspire.Hosting" />
</ItemGroup>
```

### Step 2: Suppress Experimental Warning

Since WireMock Runner is experimental, you'll need to suppress the `SAIFMOCK001` warning. See [SAIFMOCK001 diagnostic documentation](../../../reference/diagnostics/SAIFMOCK001.md) for suppression options.

### Step 3: Add WireMock Runner Resource

Create the WireMock Runner resource. The API token is automatically configured:

```csharp
var wiremockRunner = builder.AddWiremockRunner("wiremock");
```

**API Token Handling:**

- **Local Development**: Prompts for token as a secret parameter in the Aspire dashboard
- **CI/CD** (when `CI=true` env var is set): Uses `WMC_API_TOKEN` environment variable directly (no parameter prompt)

**To obtain your WireMock Cloud API token:**

1. Log in to [WireMock Cloud](https://app.wiremock.cloud)
2. Navigate to **Settings** → **API Tokens**
3. Create a new API token or copy an existing one

**CI/CD Configuration:**
Set the `WMC_API_TOKEN` environment variable in your CI/CD pipeline using `integrationTestEnvironmentVariables`:

```yaml
extends:
  template: azure-dotnet-api-pr-v3.yml@templates
  parameters:
    # ... other parameters ...
    integrationTestEnvironmentVariables:
      - name: WMC_API_TOKEN
        value: $(WmcApiToken)  # From variable group/secret
```

See [Integration Test Environment Variables](../testing/integration-test-environment-variables.md) for full parameter documentation.

### Step 4: Add Mock Services

Add individual mock services to the runner. Each mock service can either reference an existing WireMock Cloud project or be provisioned automatically:

```csharp
// Reference an existing WireMock Cloud project (using project ID)
var externalApi = wiremockRunner.AddMock(
    name: "external-api",
    port: 8080,
    wiremockProjectId: "abc123xyz"
);

// Add mock service that will be created in WireMock Cloud (no project ID)
// The provision resource will create the mock API and set organization-wide access
var newApi = wiremockRunner.AddMock(
    name: "new-api",
    port: 8082
);
```

**Finding Your WireMock Project ID** (for existing mocks):

1. Navigate to your mock in [WireMock Cloud](https://app.wiremock.cloud)
2. Copy the URL from your browser: `https://app.wiremock.cloud/mock-apis/abc123xyz/stubs/...`
3. The `wiremockProjectId` is the unique identifier in the URL (e.g., `abc123xyz`)

### Step 5: Add Seed Stubs (Optional)

For auto-provisioned mocks, provide initial stub data so the mock is immediately useful:

```csharp
var newApi = wiremockRunner.AddMock("new-api", 8082)
    .WithSeeds(Path.Combine(builder.AppHostDirectory, "seeds", "new-api"));
```

Create a `stubs.json` file in the seed directory using the WireMock export format:

```json title="seeds/new-api/stubs.json"
{
  "mappings": [
    {
      "id": "a1b2c3d4-0001-4000-8000-000000000001",
      "name": "List Animals",
      "request": { "method": "GET", "url": "/animals" },
      "response": {
        "status": 200,
        "jsonBody": [
          { "name": "dog", "type": "domestic" },
          { "name": "cat", "type": "domestic" }
        ]
      }
    }
  ]
}
```

!!! tip "Stable Stub IDs"

    Include a stable `id` (UUID) for each mapping. This makes imports idempotent — re-running provision-and-pull updates existing stubs rather than creating duplicates.

### Step 6: Reference Mocks in Your Application

Wire your application to reference the mock services:

```csharp
builder
    .AddProject<Projects.MyApiService>("my-api-service")
    .WithReference(externalApi)
    .WithReference(newApi)
    .WaitFor(externalApi)
    .WaitFor(newApi);
```

---

## 📝 Complete Example

Here's a complete AppHost configuration using WireMock Runner:

```csharp title="AppHost.cs"
using SAIF.Platform.Aspire.Hosting.WiremockRunner;

var builder = DistributedApplication.CreateBuilder(args);

// Create WireMock Runner resource
// - Local dev: prompts for token in Aspire dashboard
// - CI/CD (CI=true): reads from WMC_API_TOKEN environment variable
var wiremockRunner = builder.AddWiremockRunner("wiremock");

// Reference existing WireMock Cloud projects
var customerApi = wiremockRunner.AddMock("customer-api", 8080, "abc123xyz");
var paymentApi = wiremockRunner.AddMock("payment-api", 8081, "def456uvw");

// Auto-provision a new mock with seed stubs
var inventoryApi = wiremockRunner.AddMock("inventory-api", 8082)
    .WithSeeds(Path.Combine(builder.AppHostDirectory, "seeds", "inventory-api"));

// Wire application to mock services
builder
    .AddProject<Projects.MyApiService>("my-api-service")
    .WithReference(customerApi)
    .WithReference(paymentApi)
    .WithReference(inventoryApi)
    .WaitFor(customerApi)
    .WaitFor(paymentApi)
    .WaitFor(inventoryApi);

builder.Build().Run();
```

---

## 🔍 How It Works

### Architecture

```
┌─────────────────────────────────────────────────────────┐
│                   WireMock Runner                        │
│                                                          │
│  ┌────────────────────────────────────────────────────┐ │
│  │  wiremock-provision-and-pull  (runs on start)      │ │
│  │                                                     │ │
│  │  Phase 1: Provision                                 │ │
│  │  - Creates mock APIs in WireMock Cloud              │ │
│  │  - Grants org-wide ACL (mock_api_editor)            │ │
│  │  - Saves project IDs to .provisioned.json           │ │
│  │                                                     │ │
│  │  Phase 1.5: Import Seeds                            │ │
│  │  - Uploads stubs.json to WireMock Cloud             │ │
│  │  - Uses wiremock import CLI command                  │ │
│  │                                                     │ │
│  │  Phase 2: Pull                                      │ │
│  │  - Downloads all mock stubs from WireMock Cloud     │ │
│  │  - Saves to .wiremock/ directory as YAML            │ │
│  │                                                     │ │
│  │  Phase 2.5: Fix Ports                               │ │
│  │  - Corrects ports in wiremock.yaml to match config  │ │
│  └────────────────────┬───────────────────────────────┘ │
│               starts after completion                    │
│                        ▼                                 │
│  ┌────────────────────────────────────────────────────┐ │
│  │  wiremock-runner  (container)                      │ │
│  │  ┌──────────────┐  ┌──────────────┐               │ │
│  │  │ Mock Service │  │ Mock Service │  ...           │ │
│  │  │ Port: 8080   │  │ Port: 8082   │               │ │
│  │  └──────────────┘  └──────────────┘               │ │
│  └────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
                          │
                          ▼
┌─────────────────────────────────────────────────────────┐
│              Your Application Service                    │
│  - References mock services via WithReference()          │
│  - Uses Aspire service discovery                         │
└─────────────────────────────────────────────────────────┘
```

### Startup Flow

When the AppHost starts:

1. **Provision-and-Pull** runs automatically:
   - **Phase 1**: Creates any auto-provisioned mock APIs in WireMock Cloud (idempotent — skips if already created)
   - **Phase 1.5**: Imports seed stubs from `WithSeeds()` directories into WireMock Cloud
   - **Phase 2**: Pulls all mock stubs from WireMock Cloud to the `.wiremock/` directory
   - **Phase 2.5**: Fixes ports in `wiremock.yaml` to match the configured mock ports
2. **Runner Container** starts after provision-and-pull completes, serving stubs from the `.wiremock/` bind mount
3. **Mock resources** become `Running` in the Aspire dashboard with allocated endpoints
4. **Your application** starts after mocks are ready (via `WaitFor()`)

### Inner Loop: Editing Stubs

To iterate on mock stubs without restarting the entire AppHost:

1. **Edit** the `stubs.json` file in your seed directory
2. **Re-run** the `wiremock-provision-and-pull` resource from the Aspire dashboard (click **Start**)
3. **Runner auto-restarts** — the runner container is automatically restarted via `docker restart` after provision-and-pull completes on subsequent runs
4. **Verify restart** — check the logs of `wiremock-runner` in the Aspire dashboard for startup messages matching your re-run timestamp

!!! tip "Confirming the restart"

    The runner resource stays in the **Running** state throughout the container restart, so dashboard status alone doesn't indicate completion. Check the `wiremock-runner` logs for the WireMock startup banner to confirm new stubs are loaded.

!!! note "Why docker restart?"

    WireMock Runner loads stubs at container startup from the bind-mounted `.wiremock/` directory. Updating files on disk alone does not trigger an in-memory reload. The WireMock admin API reset endpoints (`POST /__admin/mappings/reset`) only reset to the startup state, not the current disk state. A container restart is the only reliable way to pick up changes.

### Directory Structure

The WireMock Runner organizes mock files in a structured directory:

```
.wiremock/
├── .manifest.json                # Mock metadata (generated)
├── .provisioned.json             # Provisioned project IDs (generated)
├── wiremock.yaml                 # WireMock Runner configuration (generated)
├── customer-api/
│   ├── mappings/
│   │   └── stub-mappings.yaml
│   └── __files/
├── inventory-api/
│   ├── mappings/
│   │   └── stub-mappings.yaml
│   └── __files/
└── payment-api/
    ├── mappings/
    │   └── stub-mappings.yaml
    └── __files/
```

### Seed Directory Structure

Seed stubs are stored alongside the AppHost project:

```
AppHost/
├── AppHost.cs
├── seeds/
│   └── inventory-api/
│       └── stubs.json            # WireMock export format
└── .wiremock/                     # Generated by provision-and-pull
```

---

## 🎯 Usage Scenarios

### Scenario 1: Multi-Service Development

When developing a service that depends on multiple external APIs:

```csharp
var wiremockRunner = builder.AddWiremockRunner("wiremock");

// Mock all external dependencies
var customerApi = wiremockRunner.AddMock("customer-api", 8080, "proj1");
var inventoryApi = wiremockRunner.AddMock("inventory-api", 8081, "proj2");
var shippingApi = wiremockRunner.AddMock("shipping-api", 8082, "proj3");

// Your order service references all mocks
builder.AddProject<Projects.OrderService>("order-service")
    .WithReference(customerApi)
    .WithReference(inventoryApi)
    .WithReference(shippingApi);
```

### Scenario 2: Rapid Prototyping with Seeds

Start a new mock from scratch with seed data:

```csharp
var notificationApi = wiremockRunner.AddMock("notification-api", 8083)
    .WithSeeds(Path.Combine(builder.AppHostDirectory, "seeds", "notification-api"));
```

```json title="seeds/notification-api/stubs.json"
{
  "mappings": [
    {
      "id": "b2c3d4e5-0001-4000-8000-000000000001",
      "name": "Send Notification",
      "request": { "method": "POST", "url": "/notifications" },
      "response": { "status": 202, "jsonBody": { "status": "queued" } }
    }
  ]
}
```

Edit the stubs, re-run provision-and-pull from the dashboard, and see changes immediately.

### Scenario 3: Team Collaboration

Share mock definitions across the team using WireMock Cloud:

1. **Developer A** creates/updates mocks in WireMock Cloud (or edits seed stubs and re-runs provision-and-pull)
2. **Developer B** pulls latest mocks by restarting the AppHost
3. **CI/CD** uses the same WireMock Cloud projects for integration tests

---

## 🔧 Configuration Options

### AddWiremockRunner()

Creates the WireMock Runner resource with automatic API token handling. See [Step 3](#step-3-add-wiremock-runner-resource) for token configuration details.

```csharp
var wiremockRunner = builder.AddWiremockRunner("wiremock");
```

### WithDirectory() (Optional)

Specifies the local directory for storing mock configurations (default: `./.wiremock`):

```csharp
var wiremockRunner = builder.AddWiremockRunner("wiremock")
    .WithDirectory("./mocks"); // Custom directory (optional)
```

### AddMock()

Adds a mock service to the runner. Two modes are supported:

**Reference an existing WireMock Cloud project:**

```csharp
var mock = wiremockRunner.AddMock(
    name: "service-name",           // Service discovery name
    port: 8080,                     // Container target port
    wiremockProjectId: "abc123xyz"  // WireMock Cloud project ID
);
```

**Provision a new mock API in WireMock Cloud:**

```csharp
var mock = wiremockRunner.AddMock(
    name: "service-name",           // Service discovery name + WireMock Cloud mock name
    port: 8080                      // Container target port
);
```

When `wiremockProjectId` is omitted, the provision-and-pull resource will:

1. Create the mock API in WireMock Cloud via the REST API (`POST /v1/mock-apis`)
2. Grant organization-wide access with `mock_api_editor` role
3. Save the created project ID to `.wiremock/.provisioned.json`
4. Import any seed stubs, then pull all stubs to disk

Provisioned project IDs persist in `.provisioned.json` so mock APIs are only created once.

### WithSeeds()

Specifies a directory containing a `stubs.json` file for initial stub data. Only applies to auto-provisioned mocks (those without a `wiremockProjectId`). See [Step 5](#step-5-add-seed-stubs-optional) for format details and best practices.

```csharp
var mock = wiremockRunner.AddMock("my-service", 8082)
    .WithSeeds(Path.Combine(builder.AppHostDirectory, "seeds", "my-service"));
```

---

## 🐛 Troubleshooting

### Issue: Mocks Not Loading

**Symptoms**: Mock services start but don't respond to requests

**Solutions**:

1. Verify the WireMock project ID is correct
2. Check that the API token has access to the project
3. Inspect the `.wiremock` directory to ensure files were downloaded
4. Check the Aspire dashboard for runner errors

### Issue: Authentication Failed

**Symptoms**: Error messages about invalid API token

**Solutions**:

1. **Local Development**: Enter the correct token when prompted in the Aspire dashboard
2. **CI/CD**: Verify the `WMC_API_TOKEN` environment variable is set correctly
3. Ensure the token hasn't expired in WireMock Cloud

### Issue: Port Conflicts

**Symptoms**: Mock services fail to start due to port in use

**Solutions**:

1. Ensure each mock uses a unique port
2. Check for other processes using the same ports
3. Use higher port numbers (e.g., 8080+) to avoid conflicts

### Issue: Service Discovery Not Working

**Symptoms**: Application can't resolve mock service endpoints

**Solutions**:

1. Ensure you're using `.WithReference(mockResource)` in your application
2. Verify the mock name matches what you're requesting
3. Check the Aspire dashboard to confirm service registration

### Issue: Stub Changes Not Reflected

**Symptoms**: Edited `stubs.json` but mock still returns old data

**Solutions**:

1. Verify the file was saved
2. Re-run the `wiremock-provision-and-pull` resource from the Aspire dashboard — the runner container restarts automatically
3. If auto-restart fails, manually restart the `wiremock-runner` container from the dashboard

!!! note

    WireMock Runner only reads stubs at container startup. Editing files on disk requires a container restart to take effect.

---

## 📊 Comparison with Other Mocking Approaches

| Approach                 | WireMock Runner       | WireMock CLI         | Container-based       |
| ------------------------ | --------------------- | -------------------- | --------------------- |
| **Runtime**              | Container (Docker)    | Executable           | Container             |
| **CI/Integration Tests** | ✅ Supported           | ❌ Not supported      | ✅ Supported           |
| **Setup Complexity**     | Medium                | Low                  | Low                   |
| **Authentication**       | API token (automatic) | Optional             | Not applicable        |
| **Orchestration**        | Centralized           | Per-mock             | Per-container         |
| **Seed Stubs**           | ✅ WithSeeds()         | ❌ N/A                | ⚠️ Manual setup        |
| **Team Sharing**         | ✅ Via WireMock Cloud  | ✅ Via WireMock Cloud | ⚠️ Manual file sharing |
| **Multi-Mock**           | ✅ Single resource     | ⚠️ Multiple resources | ⚠️ Multiple containers |
| **Production Ready**     | ⚠️ Experimental        | ✅ Stable             | ✅ Stable              |

---

## 🎓 Best Practices

### Organize Mock Projects by Domain

Create separate WireMock Cloud projects for different domains:

- `customer-service-mocks`
- `payment-service-mocks`
- `inventory-service-mocks`

### Use Descriptive Mock Names

Choose names that clearly indicate what's being mocked:

```csharp
var customerApiMock = wiremockRunner.AddMock("customer-api", 8080, "proj1");
// Not: var mock1 = wiremockRunner.AddMock("mock1", 8080, "proj1");
```

### Document Project IDs

Document which WireMock Cloud project ID corresponds to which service:

```csharp
// WireMock Cloud Project IDs:
// - abc123xyz: Customer API (https://app.wiremock.cloud/mock-apis/abc123xyz)
// - def456uvw: Payment API (https://app.wiremock.cloud/mock-apis/def456uvw)
var customerApi = wiremockRunner.AddMock("customer-api", 8080, "abc123xyz");
var paymentApi = wiremockRunner.AddMock("payment-api", 8081, "def456uvw");
```

### When to Use WireMock Runner vs. CLI

**Use WireMock Runner when** you need integration tests in CI/CD, container-based orchestration, centralized multi-mock management, or seed stubs for rapid prototyping.

**Use WireMock CLI when** you need a stable solution for local-only development with simple, independent mock scenarios and minimal setup.

---

## 🔗 Related Resources

- [WireMock CLI Hosting Guide](host-wiremock-cli.md)
- [Recording API Mocks](wiremock-cli-record.md)
- [SAIFMOCK001 Diagnostic](../../../reference/diagnostics/SAIFMOCK001.md)
- [Aspire Service Discovery](https://learn.microsoft.com/dotnet/aspire/service-discovery/overview)
- [WireMock Runner Foundry Example](../../../foundry/aspire-wiremockrunner.md) - Working example of container-based API mocking with Aspire

---

## 💬 Feedback

WireMock Runner is an experimental feature. Please provide feedback through:

- 🐛 **Issues** for bugs or problems
- 💡 **Feature requests** for improvements
- 📝 **Discussions** for general feedback

Visit the [Forge repository](https://github.com/saif-corp/forge/issues) to share your thoughts.
