tower_async_test/
builder.rs

1//! Builder for creating [`crate::mock::Mock`] services and testing them with a
2//! [`tower_async_layer::Layer`].
3
4use std::convert::Infallible;
5
6use tower_async_layer::Layer;
7use tower_async_service::Service;
8
9pub mod marker {
10    //! Marker types for builder state,
11    //! used to prevent invalid state transitions.
12
13    /// Marker type to indicates an unspecified Type.
14    #[derive(Debug, Default)]
15    pub struct None;
16
17    /// Marker type to indicates a defined Type.
18    #[derive(Debug, Default)]
19    pub struct Defined;
20
21    /// Marker type to indicates a successful response (used for the test type).
22    #[derive(Debug, Default)]
23    pub struct Ok<T>(pub T);
24
25    /// Marker type to indicates an error (used for the test type).
26    #[derive(Debug, Default)]
27    pub struct Err<T>(pub T);
28}
29
30/// Defines the test data structure used by the builder,
31/// to store internally the registered tests.
32#[derive(Debug)]
33pub struct Test<In, Out> {
34    output: Out,
35    expected_input: Option<In>,
36}
37
38/// Builder for creating [`crate::mock::Mock`] services and testing them with a
39/// [`tower_async_layer::Layer`].
40///
41/// This generic builder is designed to make it easy to test your own
42/// [`tower_async_layer::Layer`] in a type-safe and guided manner. It achieves
43/// this by using the "Builder" pattern and a secure state flow guided by the Rust
44/// type system.
45///
46/// # Flow
47///
48/// ```none
49///                               define another cycle
50///                               ┌───────┐
51///                               │       │
52///                               │       │
53/// ┌───────────────┐     ┌───────▼───────┴───────┐              ┌─────────────────────────┐
54/// │Define         │     │Define <Response>      │              │Execute and await the    │
55/// │<Input> Request├─────►or <Error> to send from├──────────────►test using the previously│
56/// └───────────────┘     │within the Mock Service│              │defined flow and using   │
57///                       │to your Layer.         │   ┌──────────►the passed in Layer to   │
58///                       └──────▲─────┬──────────┘   │          │generate the service used│
59///                              │     │              │          │for testing.             │
60///                    define    │     │              │          └─────────────┬───────────┘
61///                    another   │     │              │                        │
62///                    cycle     │     │              │                        │
63///                        ┌─────┴─────▼──────────────┴─┐        ┌─────────────▼─────────────┐
64///                        │Define the expected         │        │Optionally expect the final│
65///                        │<Request> received by       │        │Output or Error (Result).  │
66///                        │the Mock Service (Optional).│        │                           │
67///                        └────────────────────────────┘        └───────────────────────────┘
68/// ```
69///
70/// # Examples
71///
72/// ```
73/// use tower_async_test::Builder;
74/// use tower_async_layer::Identity;
75///
76/// #[tokio::main]
77/// async fn main() {
78///     Builder::new("ping")
79///         .send_response("pong")
80///         .expect_request("ping")
81///         .test(Identity::new())
82///         .await
83///         .expect_response("pong");
84/// }
85/// ```
86#[derive(Debug)]
87pub struct Builder<R, T, RequestState> {
88    request: R,
89    tests: T,
90    _request_state: RequestState,
91}
92
93//////////////////////////
94/// Virgin Builder
95//////////////////////////
96
97impl<R> Builder<R, marker::None, marker::None> {
98    /// Creates a new builder with the given request.
99    pub fn new(request: R) -> Self {
100        Self {
101            request,
102            tests: marker::None,
103            _request_state: marker::None,
104        }
105    }
106
107    /// Register the sending of a (successful) response.
108    pub fn send_response<Response>(
109        self,
110        response: Response,
111    ) -> Builder<R, Vec<Test<R, marker::Ok<Response>>>, marker::None> {
112        Builder {
113            request: self.request,
114            tests: vec![Test {
115                output: marker::Ok(response),
116                expected_input: None,
117            }],
118            _request_state: marker::None,
119        }
120    }
121
122    /// Register the sending of an error.
123    pub fn send_error<Error>(
124        self,
125        error: Error,
126    ) -> Builder<R, Vec<Test<R, marker::Err<Error>>>, marker::None> {
127        Builder {
128            request: self.request,
129            tests: vec![Test {
130                output: marker::Err(error),
131                expected_input: None,
132            }],
133            _request_state: marker::None,
134        }
135    }
136}
137
138//////////////////////////
139/// Ok-only test builder
140//////////////////////////
141
142impl<R, Response, RequestState> Builder<R, Vec<Test<R, marker::Ok<Response>>>, RequestState> {
143    /// Register the sending of an additional (successful) response.
144    pub fn send_response(
145        mut self,
146        response: Response,
147    ) -> Builder<R, Vec<Test<R, marker::Ok<Response>>>, marker::None> {
148        self.tests.push(Test {
149            output: marker::Ok(response),
150            expected_input: None,
151        });
152        Builder {
153            request: self.request,
154            tests: self.tests,
155            _request_state: marker::None,
156        }
157    }
158
159    /// Register the sending of an additional error.
160    #[allow(clippy::type_complexity)]
161    pub fn send_error<Error>(
162        self,
163        error: Error,
164    ) -> Builder<R, Vec<Test<R, Result<Response, Error>>>, marker::None> {
165        let mut tests: Vec<_> = self
166            .tests
167            .into_iter()
168            .map(|test| Test {
169                output: Ok(test.output.0),
170                expected_input: test.expected_input,
171            })
172            .collect();
173        tests.push(Test {
174            output: Err(error),
175            expected_input: None,
176        });
177        Builder {
178            request: self.request,
179            tests,
180            _request_state: marker::None,
181        }
182    }
183}
184
185impl<R, Response, RequestState> Builder<R, Vec<Test<R, marker::Ok<Response>>>, RequestState>
186where
187    R: Send + Sync + std::fmt::Debug + PartialEq,
188    Response: Send + Sync,
189{
190    /// Test the given [`tower_async_layer::Layer`] with the previously registered tests.
191    ///
192    /// # Panics
193    ///
194    /// Panics if there are less requests returned then there
195    /// are responses+errors registered.
196    pub async fn test<L>(
197        self,
198        layer: L,
199    ) -> ResponseTester<
200        <<L as Layer<crate::mock::Mock<R, Response, Infallible>>>::Service as Service<R>>::Response,
201        <<L as Layer<crate::mock::Mock<R, Response, Infallible>>>::Service as Service<R>>::Error,
202    >
203    where
204        L: Layer<crate::mock::Mock<R, Response, Infallible>>,
205        L::Service: Service<R>,
206    {
207        let tests = self
208            .tests
209            .into_iter()
210            .map(|test| Test {
211                output: Ok(test.output.0),
212                expected_input: test.expected_input,
213            })
214            .collect();
215        test_layer(layer, self.request, tests).await
216    }
217}
218
219impl<R, Response> Builder<R, Vec<Test<R, marker::Ok<Response>>>, marker::None> {
220    /// Register the expectation of a request,
221    /// for the same cycle as the previously added successful response.
222    pub fn expect_request(
223        mut self,
224        request: R,
225    ) -> Builder<R, Vec<Test<R, marker::Ok<Response>>>, marker::Defined> {
226        self.tests.last_mut().unwrap().expected_input = Some(request);
227        Builder {
228            request: self.request,
229            tests: self.tests,
230            _request_state: marker::Defined,
231        }
232    }
233}
234
235//////////////////////////
236/// Error-only test builder
237//////////////////////////
238
239impl<R, Error, RequestState> Builder<R, Vec<Test<R, marker::Err<Error>>>, RequestState> {
240    /// Register the sending of an additional (successful) response.
241    ///
242    #[allow(clippy::type_complexity)]
243    pub fn send_response<Response>(
244        self,
245        response: Response,
246    ) -> Builder<R, Vec<Test<R, Result<Response, Error>>>, marker::None> {
247        let mut tests: Vec<_> = self
248            .tests
249            .into_iter()
250            .map(|test| Test {
251                output: Err(test.output.0),
252                expected_input: test.expected_input,
253            })
254            .collect();
255        tests.push(Test {
256            output: Ok(response),
257            expected_input: None,
258        });
259        Builder {
260            request: self.request,
261            tests,
262            _request_state: marker::None,
263        }
264    }
265
266    /// Register the sending of an additional error.
267    pub fn send_error(
268        mut self,
269        error: Error,
270    ) -> Builder<R, Vec<Test<R, marker::Err<Error>>>, marker::None> {
271        self.tests.push(Test {
272            output: marker::Err(error),
273            expected_input: None,
274        });
275        Builder {
276            request: self.request,
277            tests: self.tests,
278            _request_state: marker::None,
279        }
280    }
281}
282
283impl<R, Error, RequestState> Builder<R, Vec<Test<R, marker::Err<Error>>>, RequestState>
284where
285    R: Send + Sync + std::fmt::Debug + PartialEq,
286    Error: Send + Sync,
287{
288    /// Test the given layer with the previously registered tests.
289    ///
290    /// # Panics
291    ///
292    /// Panics if there are less requests returned then there
293    /// are responses+errors registered.
294    pub async fn test<L>(
295        self,
296        layer: L,
297    ) -> ResponseTester<
298        <<L as Layer<crate::mock::Mock<R, (), Error>>>::Service as Service<R>>::Response,
299        <<L as Layer<crate::mock::Mock<R, (), Error>>>::Service as Service<R>>::Error,
300    >
301    where
302        L: Layer<crate::mock::Mock<R, (), Error>>,
303        L::Service: Service<R>,
304    {
305        let tests = self
306            .tests
307            .into_iter()
308            .map(|test| Test {
309                output: Err(test.output.0),
310                expected_input: test.expected_input,
311            })
312            .collect();
313        test_layer(layer, self.request, tests).await
314    }
315}
316
317impl<R, Error> Builder<R, Vec<Test<R, marker::Err<Error>>>, marker::None> {
318    /// Register the expectation of a request,
319    /// for the same cycle as the previously added error.
320    pub fn expect_request(
321        mut self,
322        request: R,
323    ) -> Builder<R, Vec<Test<R, marker::Err<Error>>>, marker::Defined> {
324        self.tests.last_mut().unwrap().expected_input = Some(request);
325        Builder {
326            request: self.request,
327            tests: self.tests,
328            _request_state: marker::Defined,
329        }
330    }
331}
332
333//////////////////////////
334/// Full Result (Ok+Err mix) test builder
335//////////////////////////
336
337impl<R, Response, Error, RequestState>
338    Builder<R, Vec<Test<R, Result<Response, Error>>>, RequestState>
339{
340    /// Register the sending of an additional (successful) response.
341    #[allow(clippy::type_complexity)]
342    pub fn send_response(
343        mut self,
344        response: Response,
345    ) -> Builder<R, Vec<Test<R, Result<Response, Error>>>, marker::None> {
346        self.tests.push(Test {
347            output: Ok(response),
348            expected_input: None,
349        });
350        Builder {
351            request: self.request,
352            tests: self.tests,
353            _request_state: marker::None,
354        }
355    }
356
357    /// Register the sending of an additional error.
358    #[allow(clippy::type_complexity)]
359    pub fn send_error(
360        mut self,
361        error: Error,
362    ) -> Builder<R, Vec<Test<R, Result<Response, Error>>>, marker::None> {
363        self.tests.push(Test {
364            output: Err(error),
365            expected_input: None,
366        });
367        Builder {
368            request: self.request,
369            tests: self.tests,
370            _request_state: marker::None,
371        }
372    }
373}
374
375impl<R, Response, Error, RequestState>
376    Builder<R, Vec<Test<R, Result<Response, Error>>>, RequestState>
377where
378    R: Send + Sync + std::fmt::Debug + PartialEq,
379    Response: Send + Sync,
380    Error: Send + Sync,
381{
382    /// Test the given layer with the previously registered tests.
383    ///
384    /// # Panics
385    ///
386    /// Panics if there are less requests returned then there
387    /// are responses+errors registered.
388    pub async fn test<L>(
389        self,
390        layer: L,
391    ) -> ResponseTester<
392        <<L as Layer<crate::mock::Mock<R, Response, Error>>>::Service as Service<R>>::Response,
393        <<L as Layer<crate::mock::Mock<R, Response, Error>>>::Service as Service<R>>::Error,
394    >
395    where
396        L: Layer<crate::mock::Mock<R, Response, Error>>,
397        L::Service: Service<R>,
398    {
399        test_layer(layer, self.request, self.tests).await
400    }
401}
402
403#[allow(clippy::type_complexity)]
404impl<R, Response, Error> Builder<R, Vec<Test<R, Result<Response, Error>>>, marker::None> {
405    /// Register the expectation of a request,
406    /// for the same cycle as the previously added result.
407    pub fn expect_request(
408        mut self,
409        request: R,
410    ) -> Builder<R, Vec<Test<R, Result<Response, Error>>>, marker::Defined> {
411        self.tests.last_mut().unwrap().expected_input = Some(request);
412        Builder {
413            request: self.request,
414            tests: self.tests,
415            _request_state: marker::Defined,
416        }
417    }
418}
419
420//////////////////////////
421/// Shared Inner Functions
422//////////////////////////
423
424async fn test_layer<L, Request, Response, Error>(
425    layer: L,
426    request: Request,
427    tests: Vec<Test<Request, Result<Response, Error>>>,
428) -> 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>
429where
430    L: Layer<crate::mock::Mock<Request, Response, Error>>,
431    L::Service: Service<Request>,
432    Request: Send + Sync + std::fmt::Debug + PartialEq,
433    Response: Send + Sync,
434    Error: Send + Sync,
435{
436    let (service, handle) = crate::mock::spawn();
437
438    let layer = layer;
439    let service = layer.layer(service);
440
441    let (input_results, expected_inputs): (Vec<_>, Vec<_>) = tests
442        .into_iter()
443        .map(|test| (test.output, test.expected_input))
444        .unzip();
445
446    {
447        let mut handle = handle.lock().await;
448        for result in input_results {
449            handle.push_result(result);
450        }
451    }
452
453    let response = service.call(request).await;
454
455    {
456        let mut handle = handle.lock().await;
457        for expected_input in expected_inputs {
458            let request = handle.pop_request();
459            if let Some(expected_request) = expected_input {
460                assert_eq!(request, expected_request);
461            }
462        }
463    }
464
465    ResponseTester::new(response)
466}
467
468//////////////////////////
469/// ResponseTester
470//////////////////////////
471
472/// Helper type for testing the response of a layer's service.
473#[derive(Debug)]
474pub struct ResponseTester<Response, Error> {
475    result: Result<Response, Error>,
476}
477
478/// Helper type for testing the response of a layer's service.
479impl<Response, Error> ResponseTester<Response, Error> {
480    /// Creates a new `ResponseTester` with the given result.
481    pub(crate) fn new(result: Result<Response, Error>) -> Self {
482        Self { result }
483    }
484}
485
486impl<Response, Error> ResponseTester<Response, Error>
487where
488    Response: PartialEq + std::fmt::Debug,
489    Error: std::fmt::Debug,
490{
491    /// Asserts that the response is equal to the given expected response.
492    ///
493    /// # Panics
494    ///
495    /// Panics if the response is an error or if the response is not equal to the given expected
496    /// response.
497    pub fn expect_response(self, expected: Response) {
498        match self.result {
499            Ok(response) => assert_eq!(response, expected),
500            Err(err) => panic!("expected response, got error: {:?}", err),
501        }
502    }
503}
504
505impl<Response, Error> ResponseTester<Response, Error>
506where
507    Response: std::fmt::Debug,
508    Error: PartialEq + std::fmt::Debug,
509{
510    /// Asserts that the response is equal to the given expected error.
511    ///
512    /// # Panics
513    ///
514    /// Panics if the response is not an error or if the error is not equal to the given expected
515    /// error.
516    pub fn expect_error(self, expected: Error) {
517        match self.result {
518            Ok(response) => panic!("expected error, got response: {:?}", response),
519            Err(err) => assert_eq!(err, expected),
520        }
521    }
522}