conduit_test/
lib.rs

1use std::borrow::Cow;
2use std::io::{Cursor, Read};
3use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
4
5use conduit::{
6    header::{HeaderValue, IntoHeaderName},
7    Body, Extensions, HeaderMap, Host, Method, Response, Scheme, StartInstant, Version,
8};
9
10pub trait ResponseExt {
11    fn into_cow(self) -> Cow<'static, [u8]>;
12}
13
14impl ResponseExt for Response<Body> {
15    /// Convert the request into a copy-on-write body
16    ///
17    /// # Blocking
18    ///
19    /// This function may block if the value is a `Body::File`.
20    ///
21    /// # Panics
22    ///
23    /// This function panics if there is an error reading a `Body::File`.
24    fn into_cow(self) -> Cow<'static, [u8]> {
25        use conduit::Body::*;
26
27        match self.into_body() {
28            Static(slice) => slice.into(),
29            Owned(vec) => vec.into(),
30            File(mut file) => {
31                let mut vec = Vec::new();
32                std::io::copy(&mut file, &mut vec).unwrap();
33                vec.into()
34            }
35        }
36    }
37}
38
39pub struct MockRequest {
40    path: String,
41    method: Method,
42    query_string: Option<String>,
43    body: Option<Vec<u8>>,
44    headers: HeaderMap,
45    extensions: Extensions,
46    reader: Option<Cursor<Vec<u8>>>,
47}
48
49impl MockRequest {
50    pub fn new(method: Method, path: &str) -> MockRequest {
51        let headers = HeaderMap::new();
52        let mut extensions = Extensions::new();
53        extensions.insert(StartInstant::now());
54
55        MockRequest {
56            path: path.to_string(),
57            extensions,
58            query_string: None,
59            body: None,
60            headers,
61            method,
62            reader: None,
63        }
64    }
65
66    pub fn with_method(&mut self, method: Method) -> &mut MockRequest {
67        self.method = method;
68        self
69    }
70
71    pub fn with_path(&mut self, path: &str) -> &mut MockRequest {
72        self.path = path.to_string();
73        self
74    }
75
76    pub fn with_query(&mut self, string: &str) -> &mut MockRequest {
77        self.query_string = Some(string.to_string());
78        self
79    }
80
81    pub fn with_body(&mut self, bytes: &[u8]) -> &mut MockRequest {
82        self.body = Some(bytes.to_vec());
83        self.reader = None;
84        self
85    }
86
87    pub fn header<K>(&mut self, name: K, value: &str) -> &mut MockRequest
88    where
89        K: IntoHeaderName,
90    {
91        self.headers
92            .insert(name, HeaderValue::from_str(value).unwrap());
93        self
94    }
95}
96
97impl conduit::RequestExt for MockRequest {
98    fn http_version(&self) -> Version {
99        Version::HTTP_11
100    }
101
102    fn method(&self) -> &Method {
103        &self.method
104    }
105    fn scheme(&self) -> Scheme {
106        Scheme::Http
107    }
108    fn host(&self) -> Host<'_> {
109        Host::Name("example.com")
110    }
111    fn virtual_root(&self) -> Option<&str> {
112        None
113    }
114
115    fn path(&self) -> &str {
116        &self.path
117    }
118
119    fn path_mut(&mut self) -> &mut String {
120        &mut self.path
121    }
122
123    fn query_string(&self) -> Option<&str> {
124        self.query_string.as_ref().map(|s| &s[..])
125    }
126
127    fn remote_addr(&self) -> SocketAddr {
128        SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 80))
129    }
130
131    fn content_length(&self) -> Option<u64> {
132        self.body.as_ref().map(|b| b.len() as u64)
133    }
134
135    fn headers(&self) -> &HeaderMap {
136        &self.headers
137    }
138
139    fn body(&mut self) -> &mut dyn Read {
140        if self.reader.is_none() {
141            let body = self.body.clone().unwrap_or_default();
142            self.reader = Some(Cursor::new(body));
143        }
144        self.reader.as_mut().unwrap()
145    }
146
147    fn extensions(&self) -> &Extensions {
148        &self.extensions
149    }
150    fn mut_extensions(&mut self) -> &mut Extensions {
151        &mut self.extensions
152    }
153}
154
155#[cfg(test)]
156mod tests {
157    use super::MockRequest;
158
159    use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
160
161    use conduit::{header, Host, Method, RequestExt, Scheme, Version};
162
163    #[test]
164    fn simple_request_test() {
165        let mut req = MockRequest::new(Method::GET, "/");
166
167        assert_eq!(req.http_version(), Version::HTTP_11);
168        assert_eq!(req.method(), Method::GET);
169        assert_eq!(req.scheme(), Scheme::Http);
170        assert_eq!(req.host(), Host::Name("example.com"));
171        assert_eq!(req.virtual_root(), None);
172        assert_eq!(req.path(), "/");
173        assert_eq!(req.query_string(), None);
174        assert_eq!(
175            req.remote_addr(),
176            SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 80))
177        );
178        assert_eq!(req.content_length(), None);
179        assert_eq!(req.headers().len(), 0);
180        let mut s = String::new();
181        req.body().read_to_string(&mut s).expect("No body");
182        assert_eq!(s, "".to_string());
183    }
184
185    #[test]
186    fn request_body_test() {
187        let mut req = MockRequest::new(Method::POST, "/articles");
188        req.with_body(b"Hello world");
189
190        assert_eq!(req.method(), Method::POST);
191        assert_eq!(req.path(), "/articles");
192        let mut s = String::new();
193        req.body().read_to_string(&mut s).expect("No body");
194        assert_eq!(s, "Hello world".to_string());
195        assert_eq!(req.content_length(), Some(11));
196    }
197
198    #[test]
199    fn request_query_test() {
200        let mut req = MockRequest::new(Method::POST, "/articles");
201        req.with_query("foo=bar");
202
203        assert_eq!(req.query_string().expect("No query string"), "foo=bar");
204    }
205
206    #[test]
207    fn request_headers() {
208        let mut req = MockRequest::new(Method::POST, "/articles");
209        req.header(header::USER_AGENT, "lulz");
210        req.header(header::DNT, "1");
211
212        assert_eq!(req.headers().len(), 2);
213        assert_eq!(req.headers().get(header::USER_AGENT).unwrap(), "lulz");
214        assert_eq!(req.headers().get(header::DNT).unwrap(), "1");
215    }
216}