use std::convert::Infallible;
use tower_async_layer::Layer;
use tower_async_service::Service;
pub mod marker {
#[derive(Debug, Default)]
pub struct None;
#[derive(Debug, Default)]
pub struct Defined;
#[derive(Debug, Default)]
pub struct Ok<T>(pub T);
#[derive(Debug, Default)]
pub struct Err<T>(pub T);
}
#[derive(Debug)]
pub struct Test<In, Out> {
output: Out,
expected_input: Option<In>,
}
#[derive(Debug)]
pub struct Builder<R, T, RequestState> {
request: R,
tests: T,
_request_state: RequestState,
}
impl<R> Builder<R, marker::None, marker::None> {
pub fn new(request: R) -> Self {
Self {
request,
tests: marker::None,
_request_state: marker::None,
}
}
pub fn send_response<Response>(
self,
response: Response,
) -> Builder<R, Vec<Test<R, marker::Ok<Response>>>, marker::None> {
Builder {
request: self.request,
tests: vec![Test {
output: marker::Ok(response),
expected_input: None,
}],
_request_state: marker::None,
}
}
pub fn send_error<Error>(
self,
error: Error,
) -> Builder<R, Vec<Test<R, marker::Err<Error>>>, marker::None> {
Builder {
request: self.request,
tests: vec![Test {
output: marker::Err(error),
expected_input: None,
}],
_request_state: marker::None,
}
}
}
impl<R, Response, RequestState> Builder<R, Vec<Test<R, marker::Ok<Response>>>, RequestState> {
pub fn send_response(
mut self,
response: Response,
) -> Builder<R, Vec<Test<R, marker::Ok<Response>>>, marker::None> {
self.tests.push(Test {
output: marker::Ok(response),
expected_input: None,
});
Builder {
request: self.request,
tests: self.tests,
_request_state: marker::None,
}
}
#[allow(clippy::type_complexity)]
pub fn send_error<Error>(
self,
error: Error,
) -> Builder<R, Vec<Test<R, Result<Response, Error>>>, marker::None> {
let mut tests: Vec<_> = self
.tests
.into_iter()
.map(|test| Test {
output: Ok(test.output.0),
expected_input: test.expected_input,
})
.collect();
tests.push(Test {
output: Err(error),
expected_input: None,
});
Builder {
request: self.request,
tests,
_request_state: marker::None,
}
}
}
impl<R, Response, RequestState> Builder<R, Vec<Test<R, marker::Ok<Response>>>, RequestState>
where
R: Send + Sync + std::fmt::Debug + PartialEq,
Response: Send + Sync,
{
pub async fn test<L>(
self,
layer: L,
) -> ResponseTester<
<<L as Layer<crate::mock::Mock<R, Response, Infallible>>>::Service as Service<R>>::Response,
<<L as Layer<crate::mock::Mock<R, Response, Infallible>>>::Service as Service<R>>::Error,
>
where
L: Layer<crate::mock::Mock<R, Response, Infallible>>,
L::Service: Service<R>,
{
let tests = self
.tests
.into_iter()
.map(|test| Test {
output: Ok(test.output.0),
expected_input: test.expected_input,
})
.collect();
test_layer(layer, self.request, tests).await
}
}
impl<R, Response> Builder<R, Vec<Test<R, marker::Ok<Response>>>, marker::None> {
pub fn expect_request(
mut self,
request: R,
) -> Builder<R, Vec<Test<R, marker::Ok<Response>>>, marker::Defined> {
self.tests.last_mut().unwrap().expected_input = Some(request);
Builder {
request: self.request,
tests: self.tests,
_request_state: marker::Defined,
}
}
}
impl<R, Error, RequestState> Builder<R, Vec<Test<R, marker::Err<Error>>>, RequestState> {
#[allow(clippy::type_complexity)]
pub fn send_response<Response>(
self,
response: Response,
) -> Builder<R, Vec<Test<R, Result<Response, Error>>>, marker::None> {
let mut tests: Vec<_> = self
.tests
.into_iter()
.map(|test| Test {
output: Err(test.output.0),
expected_input: test.expected_input,
})
.collect();
tests.push(Test {
output: Ok(response),
expected_input: None,
});
Builder {
request: self.request,
tests,
_request_state: marker::None,
}
}
pub fn send_error(
mut self,
error: Error,
) -> Builder<R, Vec<Test<R, marker::Err<Error>>>, marker::None> {
self.tests.push(Test {
output: marker::Err(error),
expected_input: None,
});
Builder {
request: self.request,
tests: self.tests,
_request_state: marker::None,
}
}
}
impl<R, Error, RequestState> Builder<R, Vec<Test<R, marker::Err<Error>>>, RequestState>
where
R: Send + Sync + std::fmt::Debug + PartialEq,
Error: Send + Sync,
{
pub async fn test<L>(
self,
layer: L,
) -> ResponseTester<
<<L as Layer<crate::mock::Mock<R, (), Error>>>::Service as Service<R>>::Response,
<<L as Layer<crate::mock::Mock<R, (), Error>>>::Service as Service<R>>::Error,
>
where
L: Layer<crate::mock::Mock<R, (), Error>>,
L::Service: Service<R>,
{
let tests = self
.tests
.into_iter()
.map(|test| Test {
output: Err(test.output.0),
expected_input: test.expected_input,
})
.collect();
test_layer(layer, self.request, tests).await
}
}
impl<R, Error> Builder<R, Vec<Test<R, marker::Err<Error>>>, marker::None> {
pub fn expect_request(
mut self,
request: R,
) -> Builder<R, Vec<Test<R, marker::Err<Error>>>, marker::Defined> {
self.tests.last_mut().unwrap().expected_input = Some(request);
Builder {
request: self.request,
tests: self.tests,
_request_state: marker::Defined,
}
}
}
impl<R, Response, Error, RequestState>
Builder<R, Vec<Test<R, Result<Response, Error>>>, RequestState>
{
#[allow(clippy::type_complexity)]
pub fn send_response(
mut self,
response: Response,
) -> Builder<R, Vec<Test<R, Result<Response, Error>>>, marker::None> {
self.tests.push(Test {
output: Ok(response),
expected_input: None,
});
Builder {
request: self.request,
tests: self.tests,
_request_state: marker::None,
}
}
#[allow(clippy::type_complexity)]
pub fn send_error(
mut self,
error: Error,
) -> Builder<R, Vec<Test<R, Result<Response, Error>>>, marker::None> {
self.tests.push(Test {
output: Err(error),
expected_input: None,
});
Builder {
request: self.request,
tests: self.tests,
_request_state: marker::None,
}
}
}
impl<R, Response, Error, RequestState>
Builder<R, Vec<Test<R, Result<Response, Error>>>, RequestState>
where
R: Send + Sync + std::fmt::Debug + PartialEq,
Response: Send + Sync,
Error: Send + Sync,
{
pub async fn test<L>(
self,
layer: L,
) -> ResponseTester<
<<L as Layer<crate::mock::Mock<R, Response, Error>>>::Service as Service<R>>::Response,
<<L as Layer<crate::mock::Mock<R, Response, Error>>>::Service as Service<R>>::Error,
>
where
L: Layer<crate::mock::Mock<R, Response, Error>>,
L::Service: Service<R>,
{
test_layer(layer, self.request, self.tests).await
}
}
#[allow(clippy::type_complexity)]
impl<R, Response, Error> Builder<R, Vec<Test<R, Result<Response, Error>>>, marker::None> {
pub fn expect_request(
mut self,
request: R,
) -> Builder<R, Vec<Test<R, Result<Response, Error>>>, marker::Defined> {
self.tests.last_mut().unwrap().expected_input = Some(request);
Builder {
request: self.request,
tests: self.tests,
_request_state: marker::Defined,
}
}
}
async fn test_layer<L, Request, Response, Error>(
layer: L,
request: Request,
tests: Vec<Test<Request, Result<Response, Error>>>,
) -> ResponseTester<<<L as Layer<crate::mock::Mock<Request, Response, Error>>>::Service as Service<Request>>::Response, <<L as Layer<crate::mock::Mock<Request, Response, Error>>>::Service as Service<Request>>::Error>
where
L: Layer<crate::mock::Mock<Request, Response, Error>>,
L::Service: Service<Request>,
Request: Send + Sync + std::fmt::Debug + PartialEq,
Response: Send + Sync,
Error: Send + Sync,
{
let (service, handle) = crate::mock::spawn();
let layer = layer;
let service = layer.layer(service);
let (input_results, expected_inputs): (Vec<_>, Vec<_>) = tests
.into_iter()
.map(|test| (test.output, test.expected_input))
.unzip();
{
let mut handle = handle.lock().await;
for result in input_results {
handle.push_result(result);
}
}
let response = service.call(request).await;
{
let mut handle = handle.lock().await;
for expected_input in expected_inputs {
let request = handle.pop_request();
if let Some(expected_request) = expected_input {
assert_eq!(request, expected_request);
}
}
}
ResponseTester::new(response)
}
#[derive(Debug)]
pub struct ResponseTester<Response, Error> {
result: Result<Response, Error>,
}
impl<Response, Error> ResponseTester<Response, Error> {
pub(crate) fn new(result: Result<Response, Error>) -> Self {
Self { result }
}
}
impl<Response, Error> ResponseTester<Response, Error>
where
Response: PartialEq + std::fmt::Debug,
Error: std::fmt::Debug,
{
pub fn expect_response(self, expected: Response) {
match self.result {
Ok(response) => assert_eq!(response, expected),
Err(err) => panic!("expected response, got error: {:?}", err),
}
}
}
impl<Response, Error> ResponseTester<Response, Error>
where
Response: std::fmt::Debug,
Error: PartialEq + std::fmt::Debug,
{
pub fn expect_error(self, expected: Error) {
match self.result {
Ok(response) => panic!("expected error, got response: {:?}", response),
Err(err) => assert_eq!(err, expected),
}
}
}