use http_body_util::{BodyExt, Full};
use httptest::{matchers::*, responders::*, Expectation, ExpectationBuilder, ServerPool};
use hyper_util::client::legacy::{connect::HttpConnector, Client, Error};
use std::{future::Future, net::SocketAddr};
fn create_test_client() -> Client<HttpConnector, Full<hyper::body::Bytes>> {
Client::builder(hyper_util::rt::TokioExecutor::new()).build_http()
}
async fn read_response_body(
resp_fut: impl Future<Output = Result<hyper::Response<hyper::body::Incoming>, Error>>,
) -> hyper::Response<hyper::body::Bytes> {
let resp = resp_fut.await.unwrap();
let (head, body) = resp.into_parts();
let bytes = body.collect().await.unwrap().to_bytes();
hyper::Response::from_parts(head, bytes)
}
#[tokio::test]
async fn test_server() {
let _ = pretty_env_logger::try_init();
let server = httptest::Server::run();
server.expect(
Expectation::matching(all_of![request::method("GET"), request::path("/foo")])
.respond_with(status_code(200)),
);
let client = create_test_client();
let resp = read_response_body(client.get(server.url("/foo"))).await;
assert_eq!(200, resp.status().as_u16());
}
#[tokio::test]
#[should_panic]
async fn test_expectation_cardinality_not_reached() {
let _ = pretty_env_logger::try_init();
let server = httptest::Server::run();
server.expect(
Expectation::matching(all_of![request::method("GET"), request::path("/foo")])
.respond_with(status_code(200)),
);
}
#[tokio::test]
#[should_panic]
async fn test_expectation_cardinality_not_reached_explicit_verify() {
let _ = pretty_env_logger::try_init();
let mut server = httptest::Server::run();
server.expect(
Expectation::matching(all_of![request::method("GET"), request::path("/foo")])
.respond_with(status_code(200)),
);
server.verify_and_clear();
}
#[tokio::test]
#[should_panic]
async fn test_expectation_cardinality_exceeded() {
let _ = pretty_env_logger::try_init();
let server = httptest::Server::run();
server.expect(
Expectation::matching(all_of![request::method("GET"), request::path("/foo")]).respond_with(
http::Response::builder()
.status(http::StatusCode::INTERNAL_SERVER_ERROR)
.body(Vec::new())
.unwrap(),
),
);
let client = create_test_client();
let resp = read_response_body(client.get(server.url("/foo"))).await;
assert_eq!(200, resp.status().as_u16());
let resp = read_response_body(client.get(server.url("/foo"))).await;
assert!(resp.status().is_server_error());
}
#[tokio::test]
async fn test_json() {
let _ = pretty_env_logger::try_init();
let my_data = serde_json::json!({
"foo": "bar",
"baz": [1, 2, 3],
});
let server = httptest::Server::run();
server.expect(
Expectation::matching(all_of![request::method("GET"), request::path("/foo")])
.respond_with(json_encoded(my_data.clone())),
);
let client = create_test_client();
let resp = read_response_body(client.get(server.url("/foo"))).await;
assert_eq!(200, resp.status().as_u16());
let body_data = serde_json::from_slice::<serde_json::Value>(resp.body()).unwrap();
assert_eq!(my_data, body_data);
assert_eq!(
Some(&b"application/json"[..]),
resp.headers().get("content-type").map(|x| x.as_bytes())
);
}
#[tokio::test]
async fn test_cycle() {
let _ = pretty_env_logger::try_init();
let server = httptest::Server::run();
server.expect(
Expectation::matching(all_of![request::method("GET"), request::path("/foo")])
.times(4)
.respond_with(cycle![status_code(200), status_code(404),]),
);
let client = create_test_client();
let resp = read_response_body(client.get(server.url("/foo"))).await;
assert_eq!(200, resp.status().as_u16());
let resp = read_response_body(client.get(server.url("/foo"))).await;
assert_eq!(404, resp.status().as_u16());
let resp = read_response_body(client.get(server.url("/foo"))).await;
assert_eq!(200, resp.status().as_u16());
let resp = read_response_body(client.get(server.url("/foo"))).await;
assert_eq!(404, resp.status().as_u16());
}
#[tokio::test]
async fn test_url_encoded() {
let _ = pretty_env_logger::try_init();
let server = httptest::Server::run();
server.expect(
Expectation::matching(all_of![
request::method("GET"),
request::path("/foo"),
request::query(url_decoded(contains(("key", "value")))),
])
.respond_with(url_encoded(&[("key", "value"), ("k", "v")])),
);
let client = create_test_client();
let resp = read_response_body(client.get(server.url("/foo?key=value"))).await;
assert_eq!(200, resp.status().as_u16());
assert_eq!(
Some(&b"application/x-www-form-urlencoded"[..]),
resp.headers().get("content-type").map(|x| x.as_bytes())
);
assert_eq!("key=value&k=v", resp.body());
}
#[tokio::test]
async fn test_respond_with_fn() {
let _ = pretty_env_logger::try_init();
let server = httptest::Server::run();
let delay = std::time::Duration::from_millis(100);
server.expect(Expectation::matching(any()).respond_with(move || {
std::thread::sleep(delay);
status_code(200)
}));
let client = create_test_client();
let now = std::time::Instant::now();
let resp = read_response_body(client.get(server.url("/foo?key=value"))).await;
let elapsed = now.elapsed();
assert_eq!(200, resp.status().as_u16());
assert!(elapsed >= delay);
}
#[tokio::test]
async fn test_delay_and_then() {
let _ = pretty_env_logger::try_init();
let server = httptest::Server::run();
let delay = std::time::Duration::from_millis(100);
server
.expect(Expectation::matching(any()).respond_with(delay_and_then(delay, status_code(200))));
let client = create_test_client();
let now = std::time::Instant::now();
let resp = read_response_body(client.get(server.url("/foo?key=value"))).await;
let elapsed = now.elapsed();
assert_eq!(200, resp.status().as_u16());
assert!(elapsed >= delay);
}
#[tokio::test]
async fn test_custom_json() {
use httptest::{matchers::*, responders::*, Expectation, Server};
use serde_json::json;
let _ = pretty_env_logger::try_init();
let server = Server::run();
#[derive(serde::Deserialize, Debug, PartialEq)]
struct PostBody {
msg: Option<String>,
}
server.expect(
Expectation::matching(all_of![
request::method("POST"),
request::path("/bar"),
request::body(json_decoded(|b: &PostBody| { b.msg.is_some() }))
])
.respond_with(json_encoded(json!({"result": "success"}))),
);
let client = create_test_client();
let post_req = http::Request::post(server.url("/bar"))
.body(json!({"msg": "foo"}).to_string().into())
.unwrap();
let resp = read_response_body(client.request(post_req)).await;
assert_eq!(200, resp.status().as_u16());
assert_eq!(
json!({"result": "success"}),
serde_json::from_slice::<serde_json::Value>(resp.body()).unwrap()
);
}
#[test]
fn test_outside_of_tokio_context() {
let _ = pretty_env_logger::try_init();
let _server = httptest::Server::run();
}
#[test]
#[should_panic]
fn test_dont_double_panic() {
let _ = pretty_env_logger::try_init();
let server = httptest::Server::run();
server.expect(
Expectation::matching(all_of![request::method("GET"), request::path("/foo")])
.respond_with(status_code(200)),
);
assert_eq!(1, 2);
}
#[tokio::test]
async fn test_server_custom() {
let _ = pretty_env_logger::try_init();
let bind_addr: SocketAddr = "127.0.0.1:0".parse().unwrap();
let server = httptest::ServerBuilder::new()
.bind_addr(bind_addr)
.run()
.unwrap();
server.expect(
Expectation::matching(all_of![request::method("GET"), request::path("/foo")])
.respond_with(status_code(200)),
);
let client = create_test_client();
let resp = read_response_body(client.get(server.url("/foo"))).await;
assert_eq!(200, resp.status().as_u16());
}
#[allow(unused)]
fn times_wrapper(times: impl httptest::IntoTimes) -> ExpectationBuilder {
Expectation::matching(request::method("POST")).times(times)
}
#[test]
fn test_server_pool_expectations_are_cleared_on_panic() {
let _ = pretty_env_logger::try_init();
static SERVER_POOL: ServerPool = ServerPool::new(1);
let _ = std::panic::catch_unwind(|| {
let server = SERVER_POOL.get_server();
server.expect(
Expectation::matching(all_of![request::method("GET"), request::path("/foo")])
.respond_with(status_code(200)),
);
panic!("panic without server expectation being met");
});
let _server = SERVER_POOL.get_server();
}