surf-vcr 0.2.0

Record and replay HTTP sessions for testing surf clients
Documentation
# Surf-vcr - Record and Replay HTTP sessions

Surf-vcr is a testing middleware for the [Surf](https://github.com/http-rs/Surf)
HTTP client library. Surf-vcr records your client's HTTP sessions with a server
to later mock the server's HTTP responses, providing deterministic testing of
your clients.

The high-level design is based on [VCR](https://github.com/vcr/vcr) for Ruby.

Source code is available on [SourceHut](https://git.sr.ht/~rjframe/surf-vcr) and
[Github](https://github.com/rjframe/surf-vcr). Patches may be sent via either
service, but the CI is running on SourceHut.


## Table of Contents

* [Introduction]#introduction
    * [Install]#application-installation
    * [Record]#record
    * [Playback]#playback
* [License]#license
* [Contributing]#contributing


## Introduction

Surf-vcr records HTTP sessions to a YAML file so you can review and modify (or
even create) the requests and responses manually. You can then inject the
pre-recorded responses into your client sessions.


### Install

You'll typically be using surf-vcr as a development dependency, so add it as
such via Cargo:

```sh
cargo add -D surf-vcr
```

Or add it to your `Cargo.toml` file manually:

```toml
[dev-dependencies]
surf-vcr = "0.2.0"
```


### Record

Either in your application or the relevant test, register the middleware with
your application in `Record` mode. You will connect to a functioning server and
record all requests and responses to a file. You can safely replay and record
multiple HTTP sessions (tests) with the same file concurrently.

Surf-vcr must be registered **after** any other middleware that modifies the
`Request` or `Response`; otherwise it will not see their modifications and
cannot record them.

I have found it useful to use a function in my application to create the Surf
client with my middleware, then call that function in my tests as well so I know
my test client and application client are identical:

```rust
fn create_surf_client() -> surf::Client {
    let session = MySessionMiddleware::new();

    surf::Client::new()
        .with(session)
}

#[cfg(test)]
mod tests {
    use super::*;
    use async_std::task;
    use surf_vcr::{VcrError, VcrMiddleware, VcrMode};

    async fn create_test_client(mode: VcrMode, cassette: &'static str)
    -> std::result::Result<surf::Client, VcrError>
    {
        let client = create_surf_client()
            .with(VcrMiddleware::new(mode, cassette).await?);

        Ok(client)
    }

    #[async_std::test]
    async fn test_example_request() {
        let client = create_test_client(
            mode::VcrMode::Record,
            "sessions/my-session.yml"
        ).await.unwrap();

        let req = surf::get("https://www.example.com")
            .insert_header("X-my-header", "stuff");

        let mut res = client.send(req).await.unwrap();
        assert_eq!(res.status(), surf::StatusCode::Ok);

        let content = res.body_string().await.unwrap();
        assert!(content.contains("illustrative examples"));
    }
}
```

Take a look at the [docs](https://docs.rs/surf-vcr/) or the
[simple](examples/simple.rs) example for more.


### Playback

To mock the server's responses simply change `VcrMode::Record` to
`VcrMode::Replay` and re-run your tests. Surf-vcr will look up each request
made, intercept it, and return the saved response.


### Modify Recorded Content

You can modify data before writing to your cassette files. This is useful while
working with sensitive or dynamic data.

```rust
VcrMiddleware::new(VcrMode::Record, path).await?
    .with_modify_request(|req| {
        req.headers
            .entry("session-key".into())
            .and_modify(|val| *val = vec!["...(erased)...".into()]);
    })
    .with_modify_response(|res| {
        res.headers
            .entry("Set-Cookie".into())
            .and_modify(|val| *val = vec!["...(erased)...".into()]);
    });
```


## License

All source code is licensed under the terms of the
[MPL 2.0 license](LICENSE.txt).


## Contributing

Patches and pull requests are welcome. For major features or breaking changes,
please open a ticket or start a discussion first so we can discuss what you
would like to do.