gitignore_template_generator/http_client/
api.rs

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