mockserver-client 7.1.0

An idiomatic Rust client for MockServer's control-plane API
Documentation
# mockserver-client

An idiomatic Rust client for [MockServer](https://www.mock-server.com)'s control-plane REST API.

## Installation

Add to your `Cargo.toml`:

```toml
[dev-dependencies]
mockserver-client = "7.0"
```

## Quick Start

```rust
use mockserver_client::{ClientBuilder, HttpRequest, HttpResponse, VerificationTimes};

fn main() -> mockserver_client::Result<()> {
    let client = ClientBuilder::new("localhost", 1080).build()?;

    // Create an expectation
    client.when(HttpRequest::new().method("GET").path("/hello"))
        .respond(HttpResponse::new().status_code(200).body("world"))?;

    // Verify the request was received
    client.verify(
        HttpRequest::new().path("/hello"),
        VerificationTimes::at_least(1),
    )?;

    // Reset all expectations
    client.reset()?;
    Ok(())
}
```

## Features

- **Fluent builder API**`client.when(request).respond(response)`
- **Response, Forward, and Error actions** — full MVP control-plane coverage
- **Verification**`verify` (count-based) and `verify_sequence` (order-based)
- **Retrieve** — recorded requests, active expectations, recorded expectations, logs
- **Clear / Reset** — by request matcher, by expectation ID, or full reset
- **Status / Bind** — query ports, bind additional ports
- **Blocking (synchronous)** — uses `reqwest` blocking client; no async runtime needed
- **TLS support** — optional HTTPS with configurable certificate verification

## API Overview

```rust
use mockserver_client::*;

let client = ClientBuilder::new("localhost", 1080).build().unwrap();

// Fluent expectation creation
client.when(HttpRequest::new().method("POST").path("/api/users"))
    .times(Times::exactly(3))
    .respond(HttpResponse::new()
        .status_code(201)
        .header("Location", "/api/users/1")
        .body(r#"{"id": 1}"#))?;

// Forward action
client.when(HttpRequest::new().path("/proxy"))
    .forward(HttpForward::new("backend.local", 8080).scheme("HTTP"))?;

// Verify
client.verify(
    HttpRequest::new().method("POST").path("/api/users"),
    VerificationTimes::between(1, 3),
)?;

// Verify sequence
client.verify_sequence(vec![
    HttpRequest::new().path("/first"),
    HttpRequest::new().path("/second"),
])?;

// Clear by request matcher
client.clear(
    Some(&HttpRequest::new().path("/api/users")),
    Some(ClearType::Expectations),
)?;

// Clear by ID
client.clear_by_id("my-expectation-id", None)?;

// Retrieve recorded requests
let requests = client.retrieve_recorded_requests(None)?;

// Retrieve active expectations
let expectations = client.retrieve_active_expectations(None)?;

// Server status
let ports = client.status()?;
println!("Listening on: {:?}", ports.ports);

// Reset everything
client.reset()?;
```

### Interactive Breakpoints

Register breakpoint matchers to pause forwarded/proxied traffic at REQUEST, RESPONSE, RESPONSE_STREAM, or INBOUND_STREAM phases. A callback WebSocket connection is opened automatically.

```rust
use mockserver_client::*;

let client = ClientBuilder::new("localhost", 1080).build()?;

// REQUEST-only breakpoint
let id = client.add_request_breakpoint(
    HttpRequest::new().path("/api/.*"),
    Box::new(|req| Some(req)),  // continue with original
)?;

// REQUEST + RESPONSE breakpoint
let id2 = client.add_request_response_breakpoint(
    HttpRequest::new().path("/api/.*"),
    Box::new(|req| Some(req)),
    Box::new(|_req, resp| Some(resp)),
)?;

// Streaming breakpoint
let id3 = client.add_stream_breakpoint(
    HttpRequest::new().path("/stream/.*"),
    &[phase::RESPONSE_STREAM],
    Box::new(|frame| {
        Some(StreamFrameDecision::continue_frame(&frame.correlation_id))
    }),
)?;

// Manage matchers
let list = client.list_breakpoint_matchers()?;
client.remove_breakpoint_matcher(&id)?;
client.clear_breakpoint_matchers()?;
client.close_breakpoint_websocket();
```

**Stream frame decisions:** `StreamFrameDecision::continue_frame`, `::modify`, `::drop_frame`, `::inject`, `::close`.

## Start / Launch MockServer

The Rust client can download and launch a local MockServer instance directly -- no Java installation and no Docker required. The launcher downloads a self-contained platform bundle (`mockserver-<version>-<os>-<arch>`) from the GitHub Release, verifies its SHA-256, caches it per-user, and starts it.

### Quick start

```rust
use mockserver_client::launcher;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut handle = launcher::start(1080)?;
    println!("MockServer running on port {}", handle.port());

    // ... use MockServer ...

    handle.stop()?;
    Ok(())
}
```

### Just ensure the binary is present

```rust
let launcher_path = launcher::ensure_launcher()?;
println!("Launcher at: {}", launcher_path.display());
```

### Specify a version

```rust
let mut handle = launcher::start_with_version(
    "7.1.0", 1080, &launcher::EnsureOptions::default()
)?;
```

### API reference

| Function / Type | Description |
|---|---|
| `launcher::ensure_launcher()` | Download, verify, cache the default-version binary, and return the launcher `PathBuf`. |
| `launcher::ensure_binary(version, opts)` | Same as above, but for a specific version. |
| `launcher::start(port)` | Ensure the binary and start MockServer at the default version. Returns a `ServerHandle`. |
| `launcher::start_with_version(version, port, opts)` | Start MockServer at a specific version. Returns a `ServerHandle`. |
| `launcher::ServerHandle` | Handle to the running process. Methods: `stop()`, `wait()`, `port()`. |
| `launcher::VERSION` | The default MockServer version, derived from `Cargo.toml` at compile time via `env!("CARGO_PKG_VERSION")`. |

### Supported platforms

| OS | Architecture |
|---|---|
| Linux | x86_64, aarch64 |
| macOS (darwin) | x86_64, aarch64 |
| Windows | x86_64, aarch64 |

### Environment variables

| Variable | Purpose |
|---|---|
| `MOCKSERVER_BINARY_BASE_URL` | Mirror host for the release assets (corporate / air-gapped networks) |
| `MOCKSERVER_BINARY_CACHE` | Override the cache directory (default: `~/.cache/mockserver/binaries` on Unix) |
| `MOCKSERVER_SKIP_BINARY_DOWNLOAD` | Fail instead of downloading (use with a pre-seeded cache in CI) |

### Version

By default the launcher downloads the MockServer version matching this crate (derived from `Cargo.toml` at compile time via `env!("CARGO_PKG_VERSION")`). Pass an explicit version to override.

## Building

```sh
cargo build
cargo test
cargo clippy
```

## Integration Tests

Integration tests require a running MockServer and are skipped by default:

```sh
# Start MockServer (e.g., via Docker)
docker run -d -p 1080:1080 mockserver/mockserver

# Run integration tests
MOCKSERVER_URL=http://localhost:1080 cargo test -- --ignored
```

## License

Apache-2.0