gitignore_template_generator/http_client/
api.rs

1use crate::ProgramExit;
2pub use crate::http_client::impls::{MockHttpClient, UreqHttpClient};
3
4/// Http client trait to make HTTP calls.
5pub trait HttpClient {
6    /// Make a GET HTTP call to given url.
7    ///
8    /// # Arguments
9    /// * `url` - the url to which to make the HTTP call.
10    ///
11    /// # Returns
12    ///
13    /// A result containing the response body of HTTP call if successful, or
14    /// a [`ProgramExit`] on error (e.g. 4xx, network issues...).
15    fn get(&self, url: &str) -> Result<String, ProgramExit>;
16}
17
18#[cfg(test)]
19mod tests {
20    use mockito::Server;
21
22    use super::*;
23    use crate::{ExitKind, constant};
24
25    mod ureq_client {
26        use super::*;
27
28        mod get {
29            use super::*;
30
31            mod success {
32                use super::*;
33
34                #[test]
35                fn it_fetches_data_as_string_when_200_response() {
36                    let mut mock_server = Server::new();
37                    let mock_body = "gitignore template for rust";
38                    let mock_uri = "/api/rust";
39                    let mock = mock_server
40                        .mock("GET", mock_uri)
41                        .with_status(200)
42                        .with_body(mock_body)
43                        .create();
44
45                    let server_url = mock_server.url();
46                    let http_client = UreqHttpClient::default();
47
48                    let actual =
49                        http_client.get(&format!("{server_url}{mock_uri}"));
50                    let expected: Result<String, ProgramExit> =
51                        Ok(String::from(mock_body));
52
53                    mock.assert();
54                    assert_eq!(actual, expected);
55                }
56
57                #[test]
58                fn it_fetches_data_as_string_from_given_server_url_when_200() {
59                    let mut mock_server = Server::new();
60                    let mock_body = "gitignore template for rust";
61                    let mock_uri = "/api/rust";
62                    let mock = mock_server
63                        .mock("GET", mock_uri)
64                        .with_status(200)
65                        .with_body(mock_body)
66                        .create();
67
68                    let server_url = mock_server.url();
69                    let http_client = UreqHttpClient { server_url };
70
71                    let actual = http_client.get(mock_uri);
72                    let expected: Result<String, ProgramExit> =
73                        Ok(String::from(mock_body));
74
75                    mock.assert();
76                    assert_eq!(actual, expected);
77                }
78            }
79
80            mod failure {
81                use super::*;
82
83                #[test]
84                fn it_fails_with_api_call_error_when_400_response() {
85                    let mut mock_server = Server::new();
86                    let mock_body = "error response";
87                    let mock_uri = "/api/rust";
88                    let mock = mock_server
89                        .mock("GET", mock_uri)
90                        .with_status(400)
91                        .with_body(mock_body)
92                        .create();
93
94                    let server_url = mock_server.url();
95                    let http_client = UreqHttpClient::default();
96
97                    let actual =
98                        http_client.get(&format!("{server_url}{mock_uri}"));
99                    let expected: Result<String, ProgramExit> =
100                        Err(ProgramExit {
101                            message: constant::error_messages::API_CALL_FAILURE
102                                .replace(
103                                    "{error}",
104                                    constant::error_messages::HTTP_400,
105                                ),
106                            exit_status: constant::exit_status::GENERIC,
107                            styled_message: None,
108                            kind: ExitKind::Error,
109                        });
110
111                    mock.assert();
112                    assert_eq!(actual, expected);
113                }
114
115                #[test]
116                fn it_fails_with_body_parsing_error_when_invalid_body() {
117                    let mut mock_server = Server::new();
118                    let mock_body = vec![0, 159, 146, 150];
119                    let mock_uri = "/api/rust";
120                    let mock = mock_server
121                        .mock("GET", mock_uri)
122                        .with_status(200)
123                        .with_body(mock_body)
124                        .create();
125
126                    let server_url = mock_server.url();
127                    let http_client = UreqHttpClient::default();
128
129                    let actual =
130                        http_client.get(&format!("{server_url}{mock_uri}"));
131                    let expected: Result<String, ProgramExit> =
132                        Err(ProgramExit {
133                            message: String::from(
134                                constant::error_messages::BODY_PARSING_ISSUE,
135                            ),
136                            exit_status:
137                                constant::exit_status::BODY_PARSING_ISSUE,
138                            styled_message: None,
139                            kind: ExitKind::Error,
140                        });
141
142                    mock.assert();
143                    assert_eq!(actual, expected);
144                }
145            }
146        }
147    }
148
149    mod mock_client {
150        use super::*;
151
152        mod get {
153            use super::*;
154
155            mod success {
156                use super::*;
157
158                #[test]
159                fn it_returns_ok_mocked_response() {
160                    let result_content = "success response";
161                    let http_client = MockHttpClient {
162                        response: Ok(String::from(result_content)),
163                    };
164
165                    let actual = http_client.get("/api/rust");
166                    let expected: Result<String, ProgramExit> =
167                        Ok(String::from(result_content));
168
169                    assert_eq!(actual, expected);
170                }
171
172                #[test]
173                fn it_returns_error_mocked_response() {
174                    let result_content = "error response";
175                    let http_client = MockHttpClient {
176                        response: Err(ProgramExit {
177                            message: String::from(result_content),
178                            exit_status: constant::exit_status::GENERIC,
179                            styled_message: None,
180                            kind: ExitKind::Error,
181                        }),
182                    };
183
184                    let actual = http_client.get("/api/rust");
185                    let expected: Result<String, ProgramExit> =
186                        Err(ProgramExit {
187                            message: String::from(result_content),
188                            exit_status: constant::exit_status::GENERIC,
189                            styled_message: None,
190                            kind: ExitKind::Error,
191                        });
192
193                    assert_eq!(actual, expected);
194                }
195            }
196        }
197    }
198}