diff --git a/src/cli/sonarr/sonarr_command_tests.rs b/src/cli/sonarr/sonarr_command_tests.rs index ae7da26..e4b0377 100644 --- a/src/cli/sonarr/sonarr_command_tests.rs +++ b/src/cli/sonarr/sonarr_command_tests.rs @@ -498,7 +498,7 @@ mod tests { mock_network .expect_handle_network_event() .with(eq::( - SonarrEvent::TriggerAutomaticEpisodeSearch(Some(expected_episode_id)).into(), + SonarrEvent::TriggerAutomaticEpisodeSearch(expected_episode_id).into(), )) .times(1) .returning(|_| { diff --git a/src/cli/sonarr/trigger_automatic_search_command_handler.rs b/src/cli/sonarr/trigger_automatic_search_command_handler.rs index e87a5a5..067b3f0 100644 --- a/src/cli/sonarr/trigger_automatic_search_command_handler.rs +++ b/src/cli/sonarr/trigger_automatic_search_command_handler.rs @@ -102,7 +102,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrTriggerAutomaticSearchCommand> SonarrTriggerAutomaticSearchCommand::Episode { episode_id } => { let resp = self .network - .handle_network_event(SonarrEvent::TriggerAutomaticEpisodeSearch(Some(episode_id)).into()) + .handle_network_event(SonarrEvent::TriggerAutomaticEpisodeSearch(episode_id).into()) .await?; serde_json::to_string_pretty(&resp)? } diff --git a/src/cli/sonarr/trigger_automatic_search_command_handler_tests.rs b/src/cli/sonarr/trigger_automatic_search_command_handler_tests.rs index 03c4057..6e2427f 100644 --- a/src/cli/sonarr/trigger_automatic_search_command_handler_tests.rs +++ b/src/cli/sonarr/trigger_automatic_search_command_handler_tests.rs @@ -233,7 +233,7 @@ mod tests { mock_network .expect_handle_network_event() .with(eq::( - SonarrEvent::TriggerAutomaticEpisodeSearch(Some(expected_episode_id)).into(), + SonarrEvent::TriggerAutomaticEpisodeSearch(expected_episode_id).into(), )) .times(1) .returning(|_| { diff --git a/src/handlers/sonarr_handlers/library/episode_details_handler.rs b/src/handlers/sonarr_handlers/library/episode_details_handler.rs index 026fa11..9d987d2 100644 --- a/src/handlers/sonarr_handlers/library/episode_details_handler.rs +++ b/src/handlers/sonarr_handlers/library/episode_details_handler.rs @@ -53,6 +53,19 @@ impl<'a, 'b> EpisodeDetailsHandler<'a, 'b> { .episode_releases, SonarrRelease ); + + fn extract_episode_id(&self) -> i64 { + self + .app + .data + .sonarr_data + .season_details_modal + .as_ref() + .expect("Season details modal is undefined") + .episodes + .current_selection() + .id + } } impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for EpisodeDetailsHandler<'a, 'b> { @@ -204,8 +217,9 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for EpisodeDetailsHandle } ActiveSonarrBlock::AutomaticallySearchEpisodePrompt => { if self.app.data.sonarr_data.prompt_confirm { - self.app.data.sonarr_data.prompt_confirm_action = - Some(SonarrEvent::TriggerAutomaticEpisodeSearch(None)); + self.app.data.sonarr_data.prompt_confirm_action = Some( + SonarrEvent::TriggerAutomaticEpisodeSearch(self.extract_episode_id()), + ); } self.app.pop_navigation_stack(); @@ -308,8 +322,9 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for EpisodeDetailsHandle if key == DEFAULT_KEYBINDINGS.confirm.key => { self.app.data.sonarr_data.prompt_confirm = true; - self.app.data.sonarr_data.prompt_confirm_action = - Some(SonarrEvent::TriggerAutomaticEpisodeSearch(None)); + self.app.data.sonarr_data.prompt_confirm_action = Some( + SonarrEvent::TriggerAutomaticEpisodeSearch(self.extract_episode_id()), + ); self.app.pop_navigation_stack(); } diff --git a/src/handlers/sonarr_handlers/library/episode_details_handler_tests.rs b/src/handlers/sonarr_handlers/library/episode_details_handler_tests.rs index c5d31f3..dfba3d3 100644 --- a/src/handlers/sonarr_handlers/library/episode_details_handler_tests.rs +++ b/src/handlers/sonarr_handlers/library/episode_details_handler_tests.rs @@ -3,14 +3,16 @@ mod tests { use crate::app::key_binding::DEFAULT_KEYBINDINGS; use crate::app::App; use crate::handlers::sonarr_handlers::library::episode_details_handler::EpisodeDetailsHandler; + use crate::handlers::sonarr_handlers::sonarr_handler_test_utils::utils::episode; use crate::handlers::KeyEventHandler; - use crate::models::servarr_data::sonarr::modals::EpisodeDetailsModal; + use crate::models::servarr_data::sonarr::modals::{EpisodeDetailsModal, SeasonDetailsModal}; use crate::models::servarr_data::sonarr::sonarr_data::sonarr_test_utils::utils::create_test_sonarr_data; use crate::models::servarr_data::sonarr::sonarr_data::{ ActiveSonarrBlock, EPISODE_DETAILS_BLOCKS, }; use crate::models::sonarr_models::SonarrReleaseDownloadBody; use crate::models::stateful_table::StatefulTable; + use pretty_assertions::assert_eq; use rstest::rstest; use strum::IntoEnumIterator; @@ -206,7 +208,7 @@ mod tests { #[rstest] #[case( ActiveSonarrBlock::AutomaticallySearchEpisodePrompt, - SonarrEvent::TriggerAutomaticEpisodeSearch(None) + SonarrEvent::TriggerAutomaticEpisodeSearch(1) )] fn test_episode_details_prompt_confirm_submit( #[case] prompt_block: ActiveSonarrBlock, @@ -221,6 +223,14 @@ mod tests { ) { let mut app = App::default(); app.data.sonarr_data = create_test_sonarr_data(); + app + .data + .sonarr_data + .season_details_modal + .as_mut() + .unwrap() + .episodes + .set_items(vec![episode()]); app.data.sonarr_data.prompt_confirm = true; app.push_navigation_stack(active_sonarr_block.into()); app.push_navigation_stack(prompt_block.into()); @@ -543,6 +553,14 @@ mod tests { ) { let mut app = App::default(); app.data.sonarr_data = create_test_sonarr_data(); + app + .data + .sonarr_data + .season_details_modal + .as_mut() + .unwrap() + .episodes + .set_items(vec![episode()]); app.data.sonarr_data.prompt_confirm = true; app.push_navigation_stack(active_sonarr_block.into()); app.push_navigation_stack(ActiveSonarrBlock::AutomaticallySearchEpisodePrompt.into()); @@ -559,7 +577,7 @@ mod tests { assert_eq!(app.get_current_route(), active_sonarr_block.into()); assert_eq!( app.data.sonarr_data.prompt_confirm_action, - Some(SonarrEvent::TriggerAutomaticEpisodeSearch(None)) + Some(SonarrEvent::TriggerAutomaticEpisodeSearch(1)) ); } @@ -607,6 +625,38 @@ mod tests { }); } + #[test] + fn test_extract_episode_id() { + let mut app = App::default(); + let mut season_details_modal = SeasonDetailsModal::default(); + season_details_modal.episodes.set_items(vec![episode()]); + app.data.sonarr_data.season_details_modal = Some(season_details_modal); + + let episode_id = EpisodeDetailsHandler::with( + DEFAULT_KEYBINDINGS.esc.key, + &mut app, + ActiveSonarrBlock::EpisodeDetails, + None, + ) + .extract_episode_id(); + + assert_eq!(episode_id, 1); + } + + #[test] + #[should_panic = "Season details modal is undefined"] + fn test_extract_episode_id_panics_when_season_details_modal_is_none() { + let mut app = App::default(); + + EpisodeDetailsHandler::with( + DEFAULT_KEYBINDINGS.esc.key, + &mut app, + ActiveSonarrBlock::EpisodeDetails, + None, + ) + .extract_episode_id(); + } + #[test] fn test_episode_details_handler_is_not_ready_when_loading() { let mut app = App::default(); diff --git a/src/handlers/sonarr_handlers/library/season_details_handler_tests.rs b/src/handlers/sonarr_handlers/library/season_details_handler_tests.rs index 0d7e764..a2707fc 100644 --- a/src/handlers/sonarr_handlers/library/season_details_handler_tests.rs +++ b/src/handlers/sonarr_handlers/library/season_details_handler_tests.rs @@ -840,6 +840,20 @@ mod tests { assert_eq!(episode_id, 1); } + #[test] + #[should_panic(expected = "Season details have not been loaded")] + fn test_extract_episode_id_panic_when_season_details_modal_is_none() { + let mut app = App::default(); + + SeasonDetailsHandler::with( + DEFAULT_KEYBINDINGS.esc.key, + &mut app, + ActiveSonarrBlock::SeasonDetails, + None, + ) + .extract_episode_id(); + } + #[test] fn test_season_details_handler_is_not_ready_when_loading() { let mut app = App::default(); diff --git a/src/network/sonarr_network.rs b/src/network/sonarr_network.rs index dc3e221..630b16e 100644 --- a/src/network/sonarr_network.rs +++ b/src/network/sonarr_network.rs @@ -88,7 +88,7 @@ pub enum SonarrEvent { TestAllIndexers, ToggleSeasonMonitoring((i64, i64)), ToggleEpisodeMonitoring(i64), - TriggerAutomaticEpisodeSearch(Option), + TriggerAutomaticEpisodeSearch(i64), TriggerAutomaticSeasonSearch(Option<(i64, i64)>), TriggerAutomaticSeriesSearch(Option), UpdateAllSeries, @@ -2230,14 +2230,13 @@ impl<'a, 'b> Network<'a, 'b> { .await } - async fn trigger_automatic_episode_search(&mut self, episode_id: Option) -> Result { + async fn trigger_automatic_episode_search(&mut self, episode_id: i64) -> Result { let event = SonarrEvent::TriggerAutomaticEpisodeSearch(episode_id); - let id = self.extract_episode_id(episode_id).await; - info!("Searching indexers for episode with ID: {id}"); + info!("Searching indexers for episode with ID: {episode_id}"); let body = SonarrCommandBody { name: "EpisodeSearch".to_owned(), - episode_ids: Some(vec![id]), + episode_ids: Some(vec![episode_id]), ..SonarrCommandBody::default() }; diff --git a/src/network/sonarr_network_tests.rs b/src/network/sonarr_network_tests.rs index 6f2b548..a487a9f 100644 --- a/src/network/sonarr_network_tests.rs +++ b/src/network/sonarr_network_tests.rs @@ -193,7 +193,7 @@ mod test { #[values( SonarrEvent::GetQueuedEvents, SonarrEvent::StartTask(SonarrTaskName::default()), - SonarrEvent::TriggerAutomaticEpisodeSearch(None), + SonarrEvent::TriggerAutomaticEpisodeSearch(0), SonarrEvent::TriggerAutomaticSeasonSearch(None), SonarrEvent::TriggerAutomaticSeriesSearch(None), SonarrEvent::UpdateAllSeries, @@ -5141,35 +5141,7 @@ mod test { })), Some(json!({})), None, - SonarrEvent::TriggerAutomaticEpisodeSearch(None), - None, - None, - ) - .await; - let mut season_details_modal = SeasonDetailsModal::default(); - season_details_modal.episodes.set_items(vec![episode()]); - app_arc.lock().await.data.sonarr_data.season_details_modal = Some(season_details_modal); - let mut network = Network::new(&app_arc, CancellationToken::new(), Client::new()); - - assert!(network - .handle_sonarr_event(SonarrEvent::TriggerAutomaticEpisodeSearch(None)) - .await - .is_ok()); - - async_server.assert_async().await; - } - - #[tokio::test] - async fn test_handle_trigger_automatic_episode_search_event_uses_provided_id() { - let (async_server, app_arc, _server) = mock_servarr_api( - RequestMethod::Post, - Some(json!({ - "name": "EpisodeSearch", - "episodeIds": [ 1 ] - })), - Some(json!({})), - None, - SonarrEvent::TriggerAutomaticEpisodeSearch(Some(1)), + SonarrEvent::TriggerAutomaticEpisodeSearch(1), None, None, ) @@ -5177,37 +5149,13 @@ mod test { let mut network = Network::new(&app_arc, CancellationToken::new(), Client::new()); assert!(network - .handle_sonarr_event(SonarrEvent::TriggerAutomaticEpisodeSearch(Some(1))) + .handle_sonarr_event(SonarrEvent::TriggerAutomaticEpisodeSearch(1)) .await .is_ok()); async_server.assert_async().await; } - #[tokio::test] - #[should_panic(expected = "Season details have not been loaded")] - async fn test_handle_trigger_automatic_episode_search_event_empty_season_details_modal_panics() { - let (_async_server, app_arc, _server) = mock_servarr_api( - RequestMethod::Post, - Some(json!({ - "name": "EpisodeSearch", - "episodeIds": [ 1 ] - })), - Some(json!({})), - None, - SonarrEvent::TriggerAutomaticEpisodeSearch(None), - None, - None, - ) - .await; - let mut network = Network::new(&app_arc, CancellationToken::new(), Client::new()); - - network - .handle_sonarr_event(SonarrEvent::TriggerAutomaticEpisodeSearch(None)) - .await - .unwrap(); - } - #[tokio::test] async fn test_handle_trigger_automatic_season_search_event() { let (async_server, app_arc, _server) = mock_servarr_api(