static_http_cache/
reqwest_mock.rs

1//! Traits describing parts of the `reqwest` library, so that we can override
2//! them in tests.
3//!
4//! You do not need to care about this module
5//! if you just want to use this crate.
6use std::error;
7use std::fmt;
8
9/// Represents the result of sending an HTTP request.
10///
11/// Modelled after `reqwest::Response`.
12pub trait HttpResponse: std::io::Read + fmt::Debug
13where
14    Self: ::std::marker::Sized,
15{
16    /// Obtain access to the headers of the response.
17    fn headers(&self) -> &reqwest::header::HeaderMap;
18
19    /// Obtain a copy of the response's status.
20    fn status(&self) -> reqwest::StatusCode;
21
22    /// Return an error if the response's status is in the range 400-599.
23    fn error_for_status(self) -> Result<Self, Box<dyn error::Error>>;
24}
25
26// impl std::io::Read for HttpResponse {
27//     #[inline]
28//     fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
29//         self.body.read(buf)
30//     }
31// }
32
33impl HttpResponse for reqwest::blocking::Response {
34    fn headers(&self) -> &reqwest::header::HeaderMap {
35        self.headers()
36    }
37    fn status(&self) -> reqwest::StatusCode {
38        self.status()
39    }
40    fn error_for_status(self) -> Result<Self, Box<dyn error::Error>> {
41        Ok(self.error_for_status()?)
42    }
43}
44
45/// Represents a thing that can send requests.
46///
47/// Modelled after `reqwest::Client`.
48pub trait Client {
49    /// Sending a request produces this kind of response.
50    type Response: HttpResponse;
51
52    /// Send the given request and return the response (or an error).
53    fn execute(
54        &self,
55        request: reqwest::blocking::Request,
56    ) -> Result<Self::Response, Box<dyn error::Error>>;
57}
58
59impl Client for reqwest::blocking::Client {
60    type Response = reqwest::blocking::Response;
61
62    fn execute(
63        &self,
64        request: reqwest::blocking::Request,
65    ) -> Result<Self::Response, Box<dyn error::Error>> {
66        Ok(self.execute(request)?)
67    }
68}
69
70#[cfg(test)]
71pub mod tests {
72    use reqwest;
73
74    use std::cell;
75    use std::fmt;
76    use std::io;
77
78    use std::error::Error;
79    use std::io::Read;
80
81    #[derive(Debug, Eq, PartialEq, Hash)]
82    pub struct FakeError;
83
84    impl fmt::Display for FakeError {
85        fn fmt(
86            &self,
87            f: &mut ::std::fmt::Formatter,
88        ) -> Result<(), ::std::fmt::Error> {
89            f.write_str("FakeError")?;
90            Ok(())
91        }
92    }
93
94    impl Error for FakeError {
95        fn description(&self) -> &str {
96            "Something Ooo occurred"
97        }
98        fn cause(&self) -> Option<&dyn Error> {
99            None
100        }
101    }
102
103    #[derive(Clone, Debug)]
104    pub struct FakeResponse {
105        pub status: reqwest::StatusCode,
106        pub headers: reqwest::header::HeaderMap,
107        pub body: io::Cursor<Vec<u8>>,
108    }
109
110    impl super::HttpResponse for FakeResponse {
111        fn headers(&self) -> &reqwest::header::HeaderMap {
112            &self.headers
113        }
114        fn status(&self) -> reqwest::StatusCode {
115            self.status
116        }
117        fn error_for_status(self) -> Result<Self, Box<dyn Error>> {
118            if !self.status.is_client_error() && !self.status.is_server_error()
119            {
120                Ok(self)
121            } else {
122                Err(Box::new(FakeError))
123            }
124        }
125    }
126
127    impl Read for FakeResponse {
128        fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
129            self.body.read(buf)
130        }
131    }
132
133    pub struct FakeClient {
134        pub expected_url: reqwest::Url,
135        pub expected_headers: reqwest::header::HeaderMap,
136        pub response: FakeResponse,
137        called: cell::Cell<bool>,
138    }
139
140    impl FakeClient {
141        pub fn new(
142            expected_url: reqwest::Url,
143            expected_headers: reqwest::header::HeaderMap,
144            response: FakeResponse,
145        ) -> FakeClient {
146            let called = cell::Cell::new(false);
147            FakeClient {
148                expected_url,
149                expected_headers,
150                response,
151                called,
152            }
153        }
154
155        pub fn assert_called(self) {
156            assert!(self.called.get());
157        }
158    }
159
160    impl super::Client for FakeClient {
161        type Response = FakeResponse;
162
163        fn execute(
164            &self,
165            request: reqwest::blocking::Request,
166        ) -> Result<Self::Response, Box<dyn Error>> {
167            assert_eq!(request.method(), &reqwest::Method::GET);
168            assert_eq!(request.url(), &self.expected_url);
169            assert_eq!(request.headers(), &self.expected_headers);
170
171            self.called.set(true);
172
173            Ok(self.response.clone())
174        }
175    }
176
177    pub struct BrokenClient<F>
178    where
179        F: Fn() -> Box<dyn Error>,
180    {
181        pub expected_url: reqwest::Url,
182        pub expected_headers: reqwest::header::HeaderMap,
183        pub make_error: F,
184        called: cell::Cell<bool>,
185    }
186
187    impl<F> BrokenClient<F>
188    where
189        F: Fn() -> Box<dyn Error>,
190    {
191        pub fn new(
192            expected_url: reqwest::Url,
193            expected_headers: reqwest::header::HeaderMap,
194            make_error: F,
195        ) -> BrokenClient<F> {
196            let called = cell::Cell::new(false);
197            BrokenClient {
198                expected_url,
199                expected_headers,
200                make_error,
201                called,
202            }
203        }
204
205        pub fn assert_called(self) {
206            assert!(self.called.get());
207        }
208    }
209
210    impl<F> super::Client for BrokenClient<F>
211    where
212        F: Fn() -> Box<dyn Error>,
213    {
214        type Response = FakeResponse;
215
216        fn execute(
217            &self,
218            request: reqwest::blocking::Request,
219        ) -> Result<Self::Response, Box<dyn Error>> {
220            assert_eq!(request.method(), &reqwest::Method::GET);
221            assert_eq!(request.url(), &self.expected_url);
222            assert_eq!(request.headers(), &self.expected_headers);
223
224            self.called.set(true);
225
226            Err((self.make_error)())
227        }
228    }
229}