diff --git a/src/models/servarr_data/sonarr/sonarr_data.rs b/src/models/servarr_data/sonarr/sonarr_data.rs index f497a86..26d3c37 100644 --- a/src/models/servarr_data/sonarr/sonarr_data.rs +++ b/src/models/servarr_data/sonarr/sonarr_data.rs @@ -26,6 +26,7 @@ pub struct SonarrData { pub history: StatefulTable, pub indexers: StatefulTable, pub indexer_settings: Option, + pub indexer_test_error: Option, pub logs: StatefulList, pub quality_profile_map: BiMap, pub queued_events: StatefulTable, @@ -51,6 +52,7 @@ impl Default for SonarrData { history: StatefulTable::default(), indexers: StatefulTable::default(), indexer_settings: None, + indexer_test_error: None, logs: StatefulList::default(), quality_profile_map: BiMap::new(), queued_events: StatefulTable::default(), diff --git a/src/models/servarr_data/sonarr/sonarr_data_tests.rs b/src/models/servarr_data/sonarr/sonarr_data_tests.rs index ca44f74..f7e1c70 100644 --- a/src/models/servarr_data/sonarr/sonarr_data_tests.rs +++ b/src/models/servarr_data/sonarr/sonarr_data_tests.rs @@ -41,6 +41,7 @@ mod tests { assert!(sonarr_data.history.is_empty()); assert!(sonarr_data.indexers.is_empty()); assert!(sonarr_data.indexer_settings.is_none()); + assert!(sonarr_data.indexer_test_error.is_none()); assert!(sonarr_data.logs.is_empty()); assert!(sonarr_data.quality_profile_map.is_empty()); assert!(sonarr_data.queued_events.is_empty()); diff --git a/src/network/radarr_network.rs b/src/network/radarr_network.rs index e2c629b..f38bdd8 100644 --- a/src/network/radarr_network.rs +++ b/src/network/radarr_network.rs @@ -256,7 +256,7 @@ impl<'a, 'b> Network<'a, 'b> { .await .map(RadarrSerdeable::from), RadarrEvent::TestIndexer(indexer_id) => self - .test_indexer(indexer_id) + .test_radarr_indexer(indexer_id) .await .map(RadarrSerdeable::from), RadarrEvent::TestAllIndexers => self.test_all_indexers().await.map(RadarrSerdeable::from), @@ -2036,7 +2036,7 @@ impl<'a, 'b> Network<'a, 'b> { .await } - async fn test_indexer(&mut self, indexer_id: Option) -> Result { + async fn test_radarr_indexer(&mut self, indexer_id: Option) -> Result { let detail_event = RadarrEvent::GetIndexers; let event = RadarrEvent::TestIndexer(None); let id = if let Some(i_id) = indexer_id { diff --git a/src/network/radarr_network_tests.rs b/src/network/radarr_network_tests.rs index edda118..1f090f2 100644 --- a/src/network/radarr_network_tests.rs +++ b/src/network/radarr_network_tests.rs @@ -862,7 +862,7 @@ mod test { } #[tokio::test] - async fn test_handle_test_indexer_event_error() { + async fn test_handle_test_radarr_indexer_event_error() { let indexer_details_json = json!({ "enableRss": true, "enableAutomaticSearch": true, @@ -938,7 +938,7 @@ mod test { } #[tokio::test] - async fn test_handle_test_indexer_event_success() { + async fn test_handle_test_radarr_indexer_event_success() { let indexer_details_json = json!({ "enableRss": true, "enableAutomaticSearch": true, @@ -1007,7 +1007,7 @@ mod test { } #[tokio::test] - async fn test_handle_test_indexer_event_success_uses_provided_id() { + async fn test_handle_test_radarr_indexer_event_success_uses_provided_id() { let indexer_details_json = json!({ "enableRss": true, "enableAutomaticSearch": true, diff --git a/src/network/sonarr_network.rs b/src/network/sonarr_network.rs index 9aa4b62..ff44911 100644 --- a/src/network/sonarr_network.rs +++ b/src/network/sonarr_network.rs @@ -67,6 +67,7 @@ pub enum SonarrEvent { ListSeries, MarkHistoryItemAsFailed(i64), StartTask(Option), + TestIndexer(Option), } impl NetworkResource for SonarrEvent { @@ -98,6 +99,7 @@ impl NetworkResource for SonarrEvent { SonarrEvent::ListSeries | SonarrEvent::GetSeriesDetails(_) => "/series", SonarrEvent::MarkHistoryItemAsFailed(_) => "/history/failed", SonarrEvent::StartTask(_) => "/command", + SonarrEvent::TestIndexer(_) => "/indexer/test", } } } @@ -224,6 +226,10 @@ impl<'a, 'b> Network<'a, 'b> { .start_sonarr_task(task_name) .await .map(SonarrSerdeable::from), + SonarrEvent::TestIndexer(indexer_id) => self + .test_sonarr_indexer(indexer_id) + .await + .map(SonarrSerdeable::from), } } @@ -1311,6 +1317,65 @@ impl<'a, 'b> Network<'a, 'b> { .await } + async fn test_sonarr_indexer(&mut self, indexer_id: Option) -> Result { + let detail_event = SonarrEvent::GetIndexers; + let event = SonarrEvent::TestIndexer(None); + let id = if let Some(i_id) = indexer_id { + i_id + } else { + self + .app + .lock() + .await + .data + .sonarr_data + .indexers + .current_selection() + .id + }; + info!("Testing Sonarr indexer with ID: {id}"); + + info!("Fetching indexer details for indexer with ID: {id}"); + + let request_props = self + .request_props_from( + detail_event, + RequestMethod::Get, + None::<()>, + Some(format!("/{id}")), + None, + ) + .await; + + let mut test_body: Value = Value::default(); + + self + .handle_request::<(), Value>(request_props, |detailed_indexer_body, _| { + test_body = detailed_indexer_body; + }) + .await?; + + info!("Testing indexer"); + + let mut request_props = self + .request_props_from(event, RequestMethod::Post, Some(test_body), None, None) + .await; + request_props.ignore_status_code = true; + + self + .handle_request::(request_props, |test_results, mut app| { + if test_results.as_object().is_none() { + app.data.sonarr_data.indexer_test_error = Some( + test_results.as_array().unwrap()[0] + .get("errorMessage") + .unwrap() + .to_string(), + ); + }; + }) + .await + } + async fn extract_series_id(&mut self, series_id: Option) -> (i64, String) { let series_id = if let Some(id) = series_id { id diff --git a/src/network/sonarr_network_tests.rs b/src/network/sonarr_network_tests.rs index d247564..b6b2025 100644 --- a/src/network/sonarr_network_tests.rs +++ b/src/network/sonarr_network_tests.rs @@ -5,6 +5,7 @@ mod test { use bimap::BiMap; use chrono::{DateTime, Utc}; use indoc::formatdoc; + use mockito::Matcher; use pretty_assertions::{assert_eq, assert_str_eq}; use reqwest::Client; use rstest::rstest; @@ -223,6 +224,7 @@ mod test { #[case(SonarrEvent::GetTasks, "/system/task")] #[case(SonarrEvent::GetUpdates, "/update")] #[case(SonarrEvent::MarkHistoryItemAsFailed(0), "/history/failed")] + #[case(SonarrEvent::TestIndexer(None), "/indexer/test")] fn test_resource(#[case] event: SonarrEvent, #[case] expected_uri: String) { assert_str_eq!(event.resource(), expected_uri); } @@ -3923,6 +3925,209 @@ mod test { } } + #[tokio::test] + async fn test_handle_test_sonarr_indexer_event_error() { + let indexer_details_json = json!({ + "enableRss": true, + "enableAutomaticSearch": true, + "enableInteractiveSearch": true, + "name": "Test Indexer", + "fields": [ + { + "name": "baseUrl", + "value": "https://test.com", + }, + { + "name": "apiKey", + "value": "", + }, + { + "name": "seedCriteria.seedRatio", + "value": "1.2", + }, + ], + "tags": [1], + "id": 1 + }); + let response_json = json!([ + { + "isWarning": false, + "propertyName": "", + "errorMessage": "test failure", + "severity": "error" + }]); + let (async_details_server, app_arc, mut server) = mock_servarr_api( + RequestMethod::Get, + None, + Some(indexer_details_json.clone()), + None, + SonarrEvent::GetIndexers, + Some("/1"), + None, + ) + .await; + let async_test_server = server + .mock( + "POST", + format!("/api/v3{}", SonarrEvent::TestIndexer(None).resource()).as_str(), + ) + .with_status(400) + .match_header("X-Api-Key", "test1234") + .match_body(Matcher::Json(indexer_details_json.clone())) + .with_body(response_json.to_string()) + .create_async() + .await; + app_arc + .lock() + .await + .data + .sonarr_data + .indexers + .set_items(vec![indexer()]); + let mut network = Network::new(&app_arc, CancellationToken::new(), Client::new()); + + if let SonarrSerdeable::Value(value) = network + .handle_sonarr_event(SonarrEvent::TestIndexer(None)) + .await + .unwrap() + { + async_details_server.assert_async().await; + async_test_server.assert_async().await; + assert_eq!( + app_arc.lock().await.data.sonarr_data.indexer_test_error, + Some("\"test failure\"".to_owned()) + ); + assert_eq!(value, response_json) + } + } + + #[tokio::test] + async fn test_handle_test_sonarr_indexer_event_success() { + let indexer_details_json = json!({ + "enableRss": true, + "enableAutomaticSearch": true, + "enableInteractiveSearch": true, + "name": "Test Indexer", + "fields": [ + { + "name": "baseUrl", + "value": "https://test.com", + }, + { + "name": "apiKey", + "value": "", + }, + { + "name": "seedCriteria.seedRatio", + "value": "1.2", + }, + ], + "tags": [1], + "id": 1 + }); + let (async_details_server, app_arc, mut server) = mock_servarr_api( + RequestMethod::Get, + None, + Some(indexer_details_json.clone()), + None, + SonarrEvent::GetIndexers, + Some("/1"), + None, + ) + .await; + let async_test_server = server + .mock( + "POST", + format!("/api/v3{}", SonarrEvent::TestIndexer(None).resource()).as_str(), + ) + .with_status(200) + .match_header("X-Api-Key", "test1234") + .match_body(Matcher::Json(indexer_details_json.clone())) + .with_body("{}") + .create_async() + .await; + app_arc + .lock() + .await + .data + .sonarr_data + .indexers + .set_items(vec![indexer()]); + let mut network = Network::new(&app_arc, CancellationToken::new(), Client::new()); + + if let SonarrSerdeable::Value(value) = network + .handle_sonarr_event(SonarrEvent::TestIndexer(None)) + .await + .unwrap() + { + async_details_server.assert_async().await; + async_test_server.assert_async().await; + assert_eq!( + app_arc.lock().await.data.sonarr_data.indexer_test_error, + None + ); + assert_eq!(value, json!({})); + } + } + + #[tokio::test] + async fn test_handle_test_sonarr_indexer_event_success_uses_provided_id() { + let indexer_details_json = json!({ + "enableRss": true, + "enableAutomaticSearch": true, + "enableInteractiveSearch": true, + "name": "Test Indexer", + "fields": [ + { + "name": "baseUrl", + "value": "https://test.com", + }, + { + "name": "apiKey", + "value": "", + }, + { + "name": "seedCriteria.seedRatio", + "value": "1.2", + }, + ], + "tags": [1], + "id": 1 + }); + let (async_details_server, app_arc, mut server) = mock_servarr_api( + RequestMethod::Get, + None, + Some(indexer_details_json.clone()), + None, + SonarrEvent::GetIndexers, + Some("/1"), + None, + ) + .await; + let async_test_server = server + .mock( + "POST", + format!("/api/v3{}", SonarrEvent::TestIndexer(None).resource()).as_str(), + ) + .with_status(200) + .match_header("X-Api-Key", "test1234") + .match_body(Matcher::Json(indexer_details_json.clone())) + .with_body("{}") + .create_async() + .await; + let mut network = Network::new(&app_arc, CancellationToken::new(), Client::new()); + + if let SonarrSerdeable::Value(value) = network + .handle_sonarr_event(SonarrEvent::TestIndexer(Some(1))) + .await + .unwrap() + { + async_details_server.assert_async().await; + async_test_server.assert_async().await; + assert_eq!(value, json!({})); + } + } + #[tokio::test] async fn test_extract_series_id() { let app_arc = Arc::new(Mutex::new(App::default()));