collective_score_client/validator/
http.rs1use 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}