collective_score_client/validator/
http.rs

1use anyhow::{bail, Context, Result};
2use reqwest::blocking::Response;
3use typed_builder::TypedBuilder;
4
5use super::{string_content::StringContent, Validator};
6
7fn response(url: &str) -> Result<Response> {
8    reqwest::blocking::get(url).with_context(|| format!("Failed to call the URL {url}"))
9}
10
11fn check_status(response: &Response, status_code: u16, url: &str) -> Result<()> {
12    if response.status() != status_code {
13        bail!("Status code mismatch for the URL {url}");
14    }
15
16    Ok(())
17}
18
19#[derive(TypedBuilder)]
20pub struct ResponseStatus<'a> {
21    url: &'a str,
22    #[builder(default = 200)]
23    status_code: u16,
24}
25
26impl<'a> Validator for ResponseStatus<'a> {
27    fn validate(&self) -> Result<()> {
28        check_status(&response(self.url)?, self.status_code, self.url)
29    }
30}
31
32#[derive(TypedBuilder)]
33pub struct ResponseBody<'a> {
34    url: &'a str,
35    #[builder(default)]
36    status_code: Option<u16>,
37    expected: StringContent<'a>,
38}
39
40impl<'a> Validator for ResponseBody<'a> {
41    fn validate(&self) -> Result<()> {
42        let response = response(self.url)?;
43
44        if let Some(status_code) = self.status_code {
45            check_status(&response, status_code, self.url)?;
46        }
47
48        let body = response.text().with_context(|| {
49            format!(
50                "Failed to read the body into a string for the URL {}",
51                self.url
52            )
53        })?;
54
55        if !self.expected.is_match(&body) {
56            bail!("Body mismatch for the URL {}", self.url);
57        }
58
59        Ok(())
60    }
61}
62
63#[cfg(test)]
64mod tests {
65    use regex::Regex;
66
67    use crate::{
68        check::{Check, CheckErr, RunnableCheck},
69        validator::{string_content::StringContent, Validator},
70    };
71
72    use super::{ResponseBody, ResponseStatus};
73
74    fn test_mo8it_com(validator: impl Validator) -> Result<(), CheckErr> {
75        Check::builder()
76            .description("Calling mo8it.com")
77            .validator(validator)
78            .hint("Maybe Mo fucked up?")
79            .build()
80            .run()
81    }
82
83    #[test]
84    fn status() {
85        test_mo8it_com(ResponseStatus::builder().url("https://mo8it.com").build()).unwrap();
86    }
87
88    #[test]
89    fn status_mismatch() {
90        test_mo8it_com(
91            ResponseStatus::builder()
92                .url("https://mo8it.com/blablablanotfound")
93                .build(),
94        )
95        .unwrap_err();
96    }
97
98    #[test]
99    fn body() {
100        test_mo8it_com(
101            ResponseBody::builder()
102                .url("https://mo8it.com")
103                .expected(StringContent::Regex(
104                    Regex::new("https://.*codeberg").unwrap(),
105                ))
106                .build(),
107        )
108        .unwrap();
109    }
110
111    #[test]
112    fn body_mismatch() {
113        test_mo8it_com(
114            ResponseBody::builder()
115                .url("https://mo8it.com")
116                .expected(StringContent::Full("bla"))
117                .build(),
118        )
119        .unwrap_err();
120    }
121}