use time::OffsetDateTime;
use crate::collectors::monitor::errors::CollectorError;
use crate::collectors::monitor::{Http, Ping};
use crate::models::monitor::config::Config;
use crate::models::monitor::measurement::{Data, Measurement};
use crate::schedule::Schedulable;
#[derive(Debug)]
pub struct Monitor {
pub id: i64,
pub host: String,
pub config: Config,
}
impl Monitor {
pub async fn measure(&self) -> Measurement {
let mut measure = Measurement {
timestamp: OffsetDateTime::now_utc(),
monitor_id: self.id,
data: None,
error: None,
};
let result: Result<Data, CollectorError> = match &self.config {
#[cfg(not(tarpaulin_include))]
Config::Ping(config) => Ping::measure(&self.host, config)
.await
.map_err(|error| error.into()),
Config::Http(config) => Http::measure(&self.host, config)
.await
.map_err(|error| error.into()),
};
if result.is_ok() {
measure.data = result.ok();
} else {
measure.error = result.err();
}
measure
}
}
impl Schedulable for Monitor {
type Id = i64;
type Interval = i64;
fn get_id(&self) -> Self::Id {
self.id
}
fn get_interval(&self) -> Self::Interval {
match &self.config {
Config::Ping(config) => config.check_frequency,
Config::Http(config) => config.check_frequency,
}
}
}
#[cfg(test)]
mod tests {
use httpmock::Method::GET;
use httpmock::MockServer;
use rstest::rstest;
use super::*;
use crate::models::monitor::config::{Header, HttpConfig, PingConfig};
#[rstest]
#[case(Config::Ping(PingConfig { check_frequency: 10, ..Default::default() }))]
#[case(Config::Http(HttpConfig { check_frequency: 10, ..Default::default() }))]
fn monitor_is_schedulable(#[case] config: Config) {
let monitor = Monitor {
id: 1,
host: String::from("test"),
config,
};
assert_eq!(monitor.get_id(), 1, "monitor id is correct");
assert_eq!(monitor.get_interval(), 10, "monitor interval is correct");
}
#[tokio::test]
async fn measure_http_with_data() {
let server = MockServer::start_async().await;
let mock = server
.mock_async(|when, then| {
when
.header("Authorization", "token")
.method(GET)
.path("/check");
then.status(200).body("index");
})
.await;
let monitor = Monitor {
id: 1,
host: format!("{}:{}", &server.host(), &server.port()),
config: Config::Http(HttpConfig {
timeout: 3,
method: String::from("GET"),
protocol: String::from("HTTP"),
path: Some(String::from("/check")),
headers: Some(vec![Header {
name: String::from("Authorization"),
value: String::from("token"),
}]),
expected_status_code: 200,
keyword: Some(String::from("index")),
..Default::default()
}),
};
let result = monitor.measure().await;
mock.assert();
assert!(
result.data.is_some() && result.error.is_none(),
"monitor measurement has data"
);
}
#[tokio::test]
async fn measure_http_with_error() {
let server = MockServer::start_async().await;
let mock = server
.mock_async(|when, then| {
when.method(GET).path("/check");
then.status(400);
})
.await;
let monitor = Monitor {
id: 1,
host: format!("{}:{}", &server.host(), &server.port()),
config: Config::Http(HttpConfig {
timeout: 3,
method: String::from("GET"),
protocol: String::from("HTTP"),
path: Some(String::from("/check")),
expected_status_code: 200,
..Default::default()
}),
};
let result = monitor.measure().await;
mock.assert();
assert!(
result.data.is_none() && result.error.is_some(),
"monitor measurement has error"
);
}
}