pub mod request;
use std::convert::TryFrom;
use std::fmt;
use std::ops::{Deref, DerefMut};
use failure::format_err;
use failure::ResultExt;
use futures::prelude::*;
use hyper::client::connect::Connect;
use hyper::client::Client;
use hyper::header::CONTENT_TYPE;
use hyper::{body, Body, Method, Response, Uri};
use log::warn;
use mime;
use tokio::time::Delay;
use crate::error::*;
pub use crate::plain::test::TestServer;
pub use request::TestRequest;
pub(crate) trait BodyReader {
fn read_body(&mut self, response: Response<Body>) -> Result<Vec<u8>>;
}
pub trait Server: Clone {
fn run_future<F, R, E>(&self, future: F) -> Result<R>
where
F: Send + 'static + Future<Output = std::result::Result<R, E>>,
R: Send + 'static,
E: failure::Fail;
fn request_expiry(&self) -> Delay;
fn run_request<F>(&self, f: F) -> Result<F::Ok>
where
F: TryFuture + Unpin + Send + 'static,
F::Error: failure::Fail + Sized,
F::Ok: Send,
{
self.run_future(
future::try_select(f, self.request_expiry().then(future::ok::<_, F::Error>))
.map_err(|either| either.factor_first().0.into())
.and_then(|might_expire| {
future::ready(match might_expire {
future::Either::Left((item, _)) => Ok(item),
future::Either::Right(_) => Err(failure::err_msg("timed out")),
})
})
.into_future()
.map(|result| result.compat()),
)
}
}
impl<T: Server> BodyReader for T {
fn read_body(&mut self, response: Response<Body>) -> Result<Vec<u8>> {
let f = body::to_bytes(response.into_body()).and_then(|b| future::ok(b.to_vec()));
self.run_future(f)
}
}
pub struct TestClient<TS: Server, C: Connect> {
pub(crate) client: Client<C, Body>,
pub(crate) test_server: TS,
}
impl<TS: Server + 'static, C: Connect + Clone + Send + Sync + 'static> TestClient<TS, C> {
pub fn head<U>(&self, uri: U) -> TestRequest<TS, C>
where
Uri: TryFrom<U>,
<Uri as TryFrom<U>>::Error: Into<http::Error>,
{
self.build_request(Method::HEAD, uri)
}
pub fn get<U>(&self, uri: U) -> TestRequest<TS, C>
where
Uri: TryFrom<U>,
<Uri as TryFrom<U>>::Error: Into<http::Error>,
{
self.build_request(Method::GET, uri)
}
pub fn options<U>(&self, uri: U) -> TestRequest<TS, C>
where
Uri: TryFrom<U>,
<Uri as TryFrom<U>>::Error: Into<http::Error>,
{
self.build_request(Method::OPTIONS, uri)
}
pub fn post<B, U>(&self, uri: U, body: B, mime: mime::Mime) -> TestRequest<TS, C>
where
B: Into<Body>,
Uri: TryFrom<U>,
<Uri as TryFrom<U>>::Error: Into<http::Error>,
{
self.build_request_with_body(Method::POST, uri, body, mime)
}
pub fn put<B, U>(&self, uri: U, body: B, mime: mime::Mime) -> TestRequest<TS, C>
where
B: Into<Body>,
Uri: TryFrom<U>,
<Uri as TryFrom<U>>::Error: Into<http::Error>,
{
self.build_request_with_body(Method::PUT, uri, body, mime)
}
pub fn patch<B, U>(&self, uri: U, body: B, mime: mime::Mime) -> TestRequest<TS, C>
where
B: Into<Body>,
Uri: TryFrom<U>,
<Uri as TryFrom<U>>::Error: Into<http::Error>,
{
self.build_request_with_body(Method::PATCH, uri, body, mime)
}
pub fn delete<U>(&self, uri: U) -> TestRequest<TS, C>
where
Uri: TryFrom<U>,
<Uri as TryFrom<U>>::Error: Into<http::Error>,
{
self.build_request(Method::DELETE, uri)
}
pub fn build_request<U>(&self, method: Method, uri: U) -> TestRequest<TS, C>
where
Uri: TryFrom<U>,
<Uri as TryFrom<U>>::Error: Into<http::Error>,
{
TestRequest::new(self, method, uri)
}
pub fn build_request_with_body<B, U>(
&self,
method: Method,
uri: U,
body: B,
mime: mime::Mime,
) -> TestRequest<TS, C>
where
B: Into<Body>,
Uri: TryFrom<U>,
<Uri as TryFrom<U>>::Error: Into<http::Error>,
{
let mut request = self.build_request(method, uri);
{
let headers = request.headers_mut();
headers.insert(CONTENT_TYPE, mime.to_string().parse().unwrap());
}
*request.body_mut() = body.into();
request
}
pub fn perform(&self, req: TestRequest<TS, C>) -> Result<TestResponse> {
let req_future = self.client.request(req.request()).map_err(|e| {
warn!("Error from test client request {:?}", e);
format_err!("request failed: {:?}", e).compat()
});
self.test_server
.run_request(req_future)
.map(|response| TestResponse {
response,
reader: Box::new(self.test_server.clone()),
})
}
}
pub struct TestResponse {
response: Response<Body>,
reader: Box<dyn BodyReader>,
}
impl Deref for TestResponse {
type Target = Response<Body>;
fn deref(&self) -> &Response<Body> {
&self.response
}
}
impl DerefMut for TestResponse {
fn deref_mut(&mut self) -> &mut Response<Body> {
&mut self.response
}
}
impl fmt::Debug for TestResponse {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "TestResponse")
}
}
impl TestResponse {
pub fn read_body(mut self) -> Result<Vec<u8>> {
self.reader.read_body(self.response)
}
pub fn read_utf8_body(self) -> Result<String> {
let buf = self.read_body()?;
let s = String::from_utf8(buf)?;
Ok(s)
}
}