tower_async_test/
lib.rs

1#![warn(
2    missing_debug_implementations,
3    missing_docs,
4    rust_2018_idioms,
5    unreachable_pub
6)]
7#![forbid(unsafe_code)]
8#![allow(elided_lifetimes_in_paths)]
9// `rustdoc::broken_intra_doc_links` is checked on CI
10
11//! This crate is to be used to test [`tower_async_layer::Layer`]s,
12//! by helping you write tests to guarantee this.
13//!
14//! The guarantees that it test are:
15//!
16//! - the [`tower_async_service::Service`] wrapped by the [`tower_async_layer::Layer`]
17//!   receives the expected requests, and that your layer react as expected on the sent responses or errors.
18//! - the [`tower_async_layer::Layer`] sends back the expected response or error.
19//!
20//! It does so by providing a [`crate::Builder`] that you can use to define the
21//! test flow and expectations. It does this by a generated [`crate::mock::Mock`] [`tower_async_service::Service`]
22//! that is used as the core [`tower_async_service::Service`] to help you
23//! test your own [`tower_async_layer::Layer`]s with the [`crate::mock::Mock`] [`tower_async_service::Service`].
24//!
25//! The [`crate::mock::Mock`] service cannot be used directly, but is instead use
26//! automatically for any _test_ spawned using the [`crate::Builder`] and specifically
27//! its [`crate::Builder::test`] method.
28//!
29//! # Examples
30//!
31//! ```
32//! use tower_async_test::Builder;
33//! use tower_async_layer::Identity;
34//!
35//! #[tokio::main]
36//! async fn main() {
37//!     Builder::new("ping")
38//!         .send_response("pong")
39//!         .expect_request("ping")
40//!         .test(Identity::new())
41//!         .await
42//!         .expect_response("pong");
43//! }
44//! ```
45
46pub mod builder;
47pub mod mock;
48
49pub use builder::Builder;
50
51#[cfg(test)]
52mod tests {
53    use std::convert::Infallible;
54
55    use super::*;
56    use tower_async_layer::{Identity, Layer};
57    use tower_async_service::Service;
58
59    #[tokio::test]
60    async fn test_runner_ok_with_success() {
61        Builder::new("ping")
62            .send_response("pong")
63            .expect_request("ping")
64            .test(Identity::new())
65            .await
66            .expect_response("pong");
67    }
68
69    #[tokio::test]
70    #[should_panic]
71    async fn test_runner_ok_with_success_panics() {
72        Builder::new("ping")
73            .send_response("pong")
74            .expect_request("pong")
75            .test(Identity::new())
76            .await
77            .expect_response("pong");
78    }
79
80    #[derive(Debug)]
81    struct ApologeticService<S> {
82        inner: S,
83    }
84
85    impl<S, Request> Service<Request> for ApologeticService<S>
86    where
87        S: Service<Request>,
88    {
89        type Response = ();
90        type Error = &'static str;
91
92        async fn call(&self, request: Request) -> Result<Self::Response, Self::Error> {
93            let _ = self.inner.call(request).await;
94            Err("Sorry!")
95        }
96    }
97
98    struct ApolgeticLayer;
99
100    impl<S> Layer<S> for ApolgeticLayer {
101        type Service = ApologeticService<S>;
102
103        fn layer(&self, inner: S) -> Self::Service {
104            ApologeticService { inner }
105        }
106    }
107
108    #[tokio::test]
109    async fn test_runner_ok_with_failure() {
110        Builder::new("ping")
111            .send_response("pong")
112            .expect_request("ping")
113            .test(ApolgeticLayer)
114            .await
115            .expect_error("Sorry!");
116    }
117
118    #[tokio::test]
119    #[should_panic]
120    async fn test_runner_ok_with_failure_panics() {
121        Builder::new("ping")
122            .send_response("pong")
123            .expect_request("ping")
124            .test(ApolgeticLayer)
125            .await
126            .expect_response(());
127    }
128
129    #[tokio::test]
130    async fn test_runner_err_with_error() {
131        Builder::new("ping")
132            .send_error("oops")
133            .expect_request("ping")
134            .test(Identity::new())
135            .await
136            .expect_error("oops");
137    }
138
139    #[tokio::test]
140    #[should_panic]
141    async fn test_runner_err_with_error_panics() {
142        Builder::new("ping")
143            .send_error("oops")
144            .expect_request("ping")
145            .test(Identity::new())
146            .await
147            .expect_response(());
148    }
149
150    #[derive(Debug)]
151    struct DebugFmtService<S> {
152        inner: S,
153    }
154
155    impl<S, Request> Service<Request> for DebugFmtService<S>
156    where
157        S: Service<Request>,
158        S::Response: std::fmt::Debug,
159        S::Error: std::fmt::Debug,
160    {
161        type Response = String;
162        type Error = Infallible;
163
164        async fn call(&self, request: Request) -> Result<Self::Response, Self::Error> {
165            Ok(format!(
166                "DebugFmtService: {:?}",
167                self.inner.call(request).await
168            ))
169        }
170    }
171
172    struct DebugFmtLayer;
173
174    impl<S> Layer<S> for DebugFmtLayer {
175        type Service = DebugFmtService<S>;
176
177        fn layer(&self, inner: S) -> Self::Service {
178            DebugFmtService { inner }
179        }
180    }
181
182    #[tokio::test]
183    async fn test_runner_err_with_response() {
184        Builder::new("ping")
185            .send_error("Sorry!")
186            .expect_request("ping")
187            .test(DebugFmtLayer)
188            .await
189            .expect_response("DebugFmtService: Err(\"Sorry!\")".to_string());
190    }
191
192    #[tokio::test]
193    #[should_panic]
194    async fn test_runner_err_with_response_panics() {
195        Builder::new("ping")
196            .send_error("Sorry!")
197            .expect_request("ping")
198            .test(DebugFmtLayer)
199            .await
200            .expect_response("Sorry!".to_string());
201    }
202}