finchers_test/
lib.rs

1//! A testing framework for Finchers.
2
3#![doc(html_root_url = "https://docs.rs/finchers-test/0.11.0")]
4#![deny(missing_docs)]
5#![deny(missing_debug_implementations)]
6#![deny(warnings)]
7
8extern crate finchers_core;
9extern crate futures;
10extern crate http;
11
12use futures::{Async, Future};
13use http::header::{HeaderName, HeaderValue};
14use http::{HttpTryFrom, Method, Request, Uri};
15use std::mem;
16
17use finchers_core::endpoint::ApplyRequest;
18use finchers_core::input::RequestBody;
19use finchers_core::{Endpoint, Error, Input, Never, Poll, Task};
20
21/// A wrapper struct of an endpoint which adds the facility for testing.
22#[derive(Debug)]
23pub struct Client<E: Endpoint> {
24    endpoint: E,
25}
26
27impl<E: Endpoint> Client<E> {
28    /// Create a new instance of `Client` from a given endpoint.
29    pub fn new(endpoint: E) -> Client<E> {
30        Client { endpoint }
31    }
32
33    /// Create a dummy request with given HTTP method and URI.
34    pub fn request<'a, M, U>(&'a self, method: M, uri: U) -> ClientRequest<'a, E>
35    where
36        Method: HttpTryFrom<M>,
37        Uri: HttpTryFrom<U>,
38    {
39        let mut client = ClientRequest {
40            client: self,
41            request: Request::new(()),
42            body: None,
43        };
44        client.method(method);
45        client.uri(uri);
46        client
47    }
48}
49
50macro_rules! impl_constructors {
51    ($(
52        $(#[$doc:meta])*
53        $METHOD:ident => $name:ident,
54    )*) => {$(
55        $(#[$doc])*
56        pub fn $name<'a, U>(&'a self, uri: U) -> ClientRequest<'a, E>
57        where
58            Uri: HttpTryFrom<U>,
59        {
60            self.request(Method::$METHOD, uri)
61        }
62    )*};
63}
64
65impl<E: Endpoint> Client<E> {
66    impl_constructors! {
67        /// Create a dummy `GET` request with given URI.
68        GET => get,
69
70        /// Create a dummy `POST` request with given URI.
71        POST => post,
72
73        /// Create a dummy `PUT` request with given URI.
74        PUT => put,
75
76        /// Create a dummy `HEAD` request with given URI.
77        HEAD => head,
78
79        /// Create a dummy `DELETE` request with given URI.
80        DELETE => delete,
81
82        /// Create a dummy `PATCH` request with given URI.
83        PATCH => patch,
84    }
85}
86
87/// A builder of dummy HTTP request.
88#[derive(Debug)]
89pub struct ClientRequest<'a, E: Endpoint + 'a> {
90    client: &'a Client<E>,
91    request: Request<()>,
92    body: Option<RequestBody>,
93}
94
95impl<'a, E: Endpoint> ClientRequest<'a, E> {
96    /// Overwrite the HTTP method of this dummy request with given value.
97    ///
98    /// # Panics
99    /// This method will panic if the parameter is invalid HTTP method.
100    pub fn method<M>(&mut self, method: M) -> &mut ClientRequest<'a, E>
101    where
102        Method: HttpTryFrom<M>,
103    {
104        *self.request.method_mut() = Method::try_from(method).ok().unwrap();
105        self
106    }
107
108    /// Overwrite the URI of this dummy request with given value.
109    ///
110    /// # Panics
111    /// This method will panic if the parameter is invalid HTTP method.
112    pub fn uri<U>(&mut self, uri: U) -> &mut ClientRequest<'a, E>
113    where
114        Uri: HttpTryFrom<U>,
115    {
116        *self.request.uri_mut() = Uri::try_from(uri).ok().unwrap();
117        self
118    }
119
120    /// Append the given header entry into this dummy request.
121    ///
122    /// # Panics
123    /// This method will panic if the given header name or value is invalid.
124    pub fn header<K, V>(&mut self, name: K, value: V) -> &mut ClientRequest<'a, E>
125    where
126        HeaderName: HttpTryFrom<K>,
127        HeaderValue: HttpTryFrom<V>,
128    {
129        let name = HeaderName::try_from(name).ok().unwrap();
130        let value = HeaderValue::try_from(value).ok().unwrap();
131        self.request.headers_mut().insert(name, value);
132        self
133    }
134
135    /// Overwrite the message body of this dummy request with given instance.
136    pub fn body(&mut self, body: RequestBody) -> &mut ClientRequest<'a, E> {
137        self.body = Some(body);
138        self
139    }
140
141    fn take(&mut self) -> ClientRequest<'a, E> {
142        mem::replace(
143            self,
144            ClientRequest {
145                client: self.client,
146                request: http::Request::new(()),
147                body: None,
148            },
149        )
150    }
151
152    /// Apply this dummy request to the associated endpoint and get its response.
153    pub fn run(&mut self) -> Result<E::Output, Error> {
154        let ClientRequest { client, request, body } = self.take();
155
156        let input = Input::new(request);
157        let body = body.unwrap_or_else(RequestBody::empty);
158
159        let apply = client.endpoint.apply_request(&input, body);
160        let task = TestFuture { apply, input };
161
162        // TODO: replace with futures::executor
163        task.wait().expect("Apply never fails")
164    }
165}
166
167struct TestFuture<T> {
168    apply: ApplyRequest<T>,
169    input: Input,
170}
171
172impl<T: Task> Future for TestFuture<T> {
173    type Item = Result<T::Output, Error>;
174    type Error = Never;
175
176    fn poll(&mut self) -> Result<Async<Self::Item>, Self::Error> {
177        match self.apply.poll_ready(&self.input) {
178            Poll::Pending => Ok(Async::NotReady),
179            Poll::Ready(ready) => Ok(Async::Ready(ready)),
180        }
181    }
182}