diff --git a/src/network/mod.rs b/src/network/mod.rs index 09bbf96..20ba2f6 100644 --- a/src/network/mod.rs +++ b/src/network/mod.rs @@ -6,6 +6,7 @@ use log::{debug, error}; use reqwest::{Client, RequestBuilder}; use serde::de::DeserializeOwned; use serde::Serialize; +use strum_macros::Display; use tokio::sync::{Mutex, MutexGuard}; use crate::app::App; @@ -59,7 +60,11 @@ impl<'a> Network<'a> { } Err(e) => { error!("Failed to parse response! {:?}", e); - self.app.lock().await.handle_error(anyhow!(e)); + self + .app + .lock() + .await + .handle_error(anyhow!("Failed to parse response! {:?}", e)); } }, RequestMethod::Delete | RequestMethod::Post => (), @@ -77,7 +82,11 @@ impl<'a> Network<'a> { } Err(e) => { error!("Failed to send request. {:?}", e); - self.app.lock().await.handle_error(anyhow!(e)); + self + .app + .lock() + .await + .handle_error(anyhow!("Failed to send request. {} ", e)); } } } @@ -111,7 +120,7 @@ impl<'a> Network<'a> { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Display, PartialEq, Eq)] pub enum RequestMethod { Get, Post, @@ -128,17 +137,23 @@ pub struct RequestProps { #[cfg(test)] mod tests { + use std::fmt::Debug; + use std::string::ToString; use std::sync::Arc; - use mockito::Server; + use mockito::{Mock, Server}; + use pretty_assertions::assert_str_eq; + use rstest::rstest; + use serde::{Deserialize, Serialize}; use tokio::sync::Mutex; use crate::app::{App, RadarrConfig}; + use crate::models::HorizontallyScrollableText; use crate::network::radarr_network::RadarrEvent; - use crate::network::Network; + use crate::network::{Network, RequestMethod, RequestProps}; #[tokio::test] - async fn test_handle_network_event() { + async fn test_handle_network_event_radarr_event() { let mut server = Server::new_async().await; let radarr_server = server .mock("GET", "/api/v3/health") @@ -169,4 +184,209 @@ mod tests { radarr_server.assert_async().await; assert!(!app_arc.lock().await.is_loading); } + + #[rstest] + #[tokio::test] + async fn test_handle_request_no_response_body( + #[values(RequestMethod::Post, RequestMethod::Delete)] request_method: RequestMethod, + ) { + let mut server = Server::new_async().await; + let async_server = server + .mock(&request_method.to_string().to_uppercase(), "/test") + .match_header("X-Api-Key", "test1234") + .with_status(200) + .create_async() + .await; + let app_arc = Arc::new(Mutex::new(App::default())); + let network = Network::new(reqwest::Client::new(), &app_arc); + + network + .handle_request::( + RequestProps { + uri: format!("{}/test", server.url()), + method: request_method, + body: Some(Test { + value: "Test".to_owned(), + }), + api_token: "test1234".to_owned(), + }, + |_, _| (), + ) + .await; + + async_server.assert_async().await; + } + + #[tokio::test] + async fn test_handle_request_get() { + let (async_server, app_arc, url) = mock_api(RequestMethod::Get, 200, true).await; + let network = Network::new(reqwest::Client::new(), &app_arc); + + network + .handle_request::<(), Test>( + RequestProps { + uri: format!("{}/test", url), + method: RequestMethod::Get, + body: None, + api_token: "test1234".to_owned(), + }, + |response, mut app| app.error = HorizontallyScrollableText::from(response.value), + ) + .await; + + async_server.assert_async().await; + assert_str_eq!(app_arc.lock().await.error.stationary_style(), "Test"); + } + + #[tokio::test] + async fn test_handle_request_get_invalid_body() { + let mut server = Server::new_async().await; + let async_server = server + .mock("GET", "/test") + .match_header("X-Api-Key", "test1234") + .with_status(200) + .with_body(r#"{ "invalid": "INVALID" }"#) + .create_async() + .await; + let app_arc = Arc::new(Mutex::new(App::default())); + let network = Network::new(reqwest::Client::new(), &app_arc); + + network + .handle_request::<(), Test>( + RequestProps { + uri: format!("{}/test", server.url()), + method: RequestMethod::Get, + body: None, + api_token: "test1234".to_owned(), + }, + |response, mut app| app.error = HorizontallyScrollableText::from(response.value), + ) + .await; + + async_server.assert_async().await; + assert!(app_arc + .lock() + .await + .error + .stationary_style() + .starts_with("Failed to parse response!")); + } + + #[tokio::test] + async fn test_handle_request_failure_to_send_request() { + let app_arc = Arc::new(Mutex::new(App::default())); + let network = Network::new(reqwest::Client::new(), &app_arc); + + network + .handle_request::<(), Test>( + RequestProps { + uri: String::default(), + method: RequestMethod::Get, + body: None, + api_token: "test1234".to_owned(), + }, + |response, mut app| app.error = HorizontallyScrollableText::from(response.value), + ) + .await; + + assert!(app_arc + .lock() + .await + .error + .stationary_style() + .starts_with("Failed to send request.")); + } + + #[rstest] + #[tokio::test] + async fn test_handle_request_non_success_code( + #[values(RequestMethod::Get, RequestMethod::Post, RequestMethod::Delete)] + request_method: RequestMethod, + ) { + let (async_server, app_arc, url) = mock_api(request_method.clone(), 404, true).await; + let network = Network::new(reqwest::Client::new(), &app_arc); + + network + .handle_request::<(), Test>( + RequestProps { + uri: format!("{}/test", url), + method: request_method, + body: None, + api_token: "test1234".to_owned(), + }, + |response, mut app| app.error = HorizontallyScrollableText::from(response.value), + ) + .await; + + async_server.assert_async().await; + assert_str_eq!( + app_arc.lock().await.error.stationary_style(), + "Request failed. Received 404 Not Found response code" + ); + } + + #[rstest] + #[tokio::test] + async fn test_call_api( + #[values(RequestMethod::Get, RequestMethod::Post, RequestMethod::Delete)] + request_method: RequestMethod, + ) { + let mut server = Server::new_async().await; + let mut async_server = server + .mock(&request_method.to_string().to_uppercase(), "/test") + .match_header("X-Api-Key", "test1234") + .with_status(200); + let mut body = None::; + + if request_method == RequestMethod::Post { + async_server = async_server.with_body(r#"{ "value": "Test" }"#); + body = Some(Test { + value: "Test".to_owned(), + }); + } + + async_server = async_server.create_async().await; + let app_arc = Arc::new(Mutex::new(App::default())); + let network = Network::new(reqwest::Client::new(), &app_arc); + + network + .call_api(RequestProps { + uri: format!("{}/test", server.url()), + method: request_method, + body, + api_token: "test1234".to_owned(), + }) + .await + .send() + .await + .unwrap(); + + async_server.assert_async().await; + } + + #[derive(Serialize, Deserialize, Debug, Default)] + struct Test { + pub value: String, + } + + async fn mock_api( + method: RequestMethod, + response_status: usize, + has_response_body: bool, + ) -> (Mock, Arc>, String) { + let mut server = Server::new_async().await; + let mut async_server = server + .mock(&method.to_string().to_uppercase(), "/test") + .match_header("X-Api-Key", "test1234") + .with_status(response_status); + + if has_response_body { + async_server = async_server.with_body(r#"{ "value": "Test" }"#); + } + + async_server = async_server.create_async().await; + let app_arc = Arc::new(Mutex::new(App::default())); + + (async_server, app_arc, server.url()) + } }