From aece20af4772073082d92d3dd0d2dde1441f0017 Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Tue, 17 Dec 2024 22:33:10 -0700 Subject: [PATCH] fix(sonarr): Pass the episode file ID alongside all DeleteEpisodeFile events when publishing to the networking channel --- src/cli/sonarr/delete_command_handler.rs | 2 +- .../sonarr/delete_command_handler_tests.rs | 31 ++++++++++ .../library/season_details_handler.rs | 17 +++++- .../library/season_details_handler_tests.rs | 34 ++++++++++- src/network/sonarr_network.rs | 27 ++------- src/network/sonarr_network_tests.rs | 59 +------------------ 6 files changed, 86 insertions(+), 84 deletions(-) diff --git a/src/cli/sonarr/delete_command_handler.rs b/src/cli/sonarr/delete_command_handler.rs index 8e2618b..2b356cc 100644 --- a/src/cli/sonarr/delete_command_handler.rs +++ b/src/cli/sonarr/delete_command_handler.rs @@ -108,7 +108,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrDeleteCommand> for SonarrDeleteComm SonarrDeleteCommand::EpisodeFile { episode_file_id } => { let resp = self .network - .handle_network_event(SonarrEvent::DeleteEpisodeFile(Some(episode_file_id)).into()) + .handle_network_event(SonarrEvent::DeleteEpisodeFile(episode_file_id).into()) .await?; serde_json::to_string_pretty(&resp)? } diff --git a/src/cli/sonarr/delete_command_handler_tests.rs b/src/cli/sonarr/delete_command_handler_tests.rs index 51fcec4..346ae9e 100644 --- a/src/cli/sonarr/delete_command_handler_tests.rs +++ b/src/cli/sonarr/delete_command_handler_tests.rs @@ -351,6 +351,37 @@ mod tests { assert!(result.is_ok()); } + #[tokio::test] + async fn test_handle_delete_episode_file_command() { + let expected_episode_file_id = 1; + let mut mock_network = MockNetworkTrait::new(); + mock_network + .expect_handle_network_event() + .with(eq::( + SonarrEvent::DeleteEpisodeFile(expected_episode_file_id).into(), + )) + .times(1) + .returning(|_| { + Ok(Serdeable::Sonarr(SonarrSerdeable::Value( + json!({"testResponse": "response"}), + ))) + }); + let app_arc = Arc::new(Mutex::new(App::default())); + let delete_episode_file_command = SonarrDeleteCommand::EpisodeFile { + episode_file_id: 1, + }; + + let result = SonarrDeleteCommandHandler::with( + &app_arc, + delete_episode_file_command, + &mut mock_network, + ) + .handle() + .await; + + assert!(result.is_ok()); + } + #[tokio::test] async fn test_handle_delete_indexer_command() { let expected_indexer_id = 1; diff --git a/src/handlers/sonarr_handlers/library/season_details_handler.rs b/src/handlers/sonarr_handlers/library/season_details_handler.rs index c66ab09..8b879d8 100644 --- a/src/handlers/sonarr_handlers/library/season_details_handler.rs +++ b/src/handlers/sonarr_handlers/library/season_details_handler.rs @@ -65,6 +65,19 @@ impl<'a, 'b> SeasonDetailsHandler<'a, 'b> { .season_releases, SonarrRelease ); + + fn extract_episode_file_id(&self) -> i64 { + self + .app + .data + .sonarr_data + .season_details_modal + .as_ref() + .expect("Season details have not been loaded") + .episodes + .current_selection() + .episode_file_id + } } impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for SeasonDetailsHandler<'a, 'b> { @@ -234,7 +247,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for SeasonDetailsHandler ActiveSonarrBlock::DeleteEpisodeFilePrompt => { if self.app.data.sonarr_data.prompt_confirm { self.app.data.sonarr_data.prompt_confirm_action = - Some(SonarrEvent::DeleteEpisodeFile(None)); + Some(SonarrEvent::DeleteEpisodeFile(self.extract_episode_file_id())); } self.app.pop_navigation_stack(); @@ -374,7 +387,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for SeasonDetailsHandler ActiveSonarrBlock::DeleteEpisodeFilePrompt if key == DEFAULT_KEYBINDINGS.confirm.key => { self.app.data.sonarr_data.prompt_confirm = true; self.app.data.sonarr_data.prompt_confirm_action = - Some(SonarrEvent::DeleteEpisodeFile(None)); + Some(SonarrEvent::DeleteEpisodeFile(self.extract_episode_file_id())); self.app.pop_navigation_stack(); } 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 dfe2a88..19f0a42 100644 --- a/src/handlers/sonarr_handlers/library/season_details_handler_tests.rs +++ b/src/handlers/sonarr_handlers/library/season_details_handler_tests.rs @@ -14,7 +14,7 @@ mod tests { use crate::models::servarr_models::{Language, Quality, QualityWrapper}; use crate::models::sonarr_models::{SonarrRelease, SonarrReleaseDownloadBody}; use crate::models::HorizontallyScrollableText; - use pretty_assertions::assert_str_eq; + use pretty_assertions::{assert_str_eq, assert_eq}; use rstest::rstest; use serde_json::Number; use std::cmp::Ordering; @@ -279,7 +279,7 @@ mod tests { )] #[case( ActiveSonarrBlock::DeleteEpisodeFilePrompt, - SonarrEvent::DeleteEpisodeFile(None) + SonarrEvent::DeleteEpisodeFile(0) )] fn test_season_details_prompt_confirm_submit( #[case] prompt_block: ActiveSonarrBlock, @@ -708,7 +708,7 @@ mod tests { )] #[case( ActiveSonarrBlock::DeleteEpisodeFilePrompt, - SonarrEvent::DeleteEpisodeFile(None) + SonarrEvent::DeleteEpisodeFile(0) )] fn test_season_details_prompt_confirm_confirm_key( #[case] prompt_block: ActiveSonarrBlock, @@ -782,6 +782,34 @@ mod tests { } }); } + + #[test] + fn test_extract_episode_file_id() { + let mut app = App::default(); + app.data.sonarr_data = create_test_sonarr_data(); + + let episode_file_id = SeasonDetailsHandler::with( + DEFAULT_KEYBINDINGS.esc.key, + &mut app, + ActiveSonarrBlock::SeasonDetails, + None, + ).extract_episode_file_id(); + + assert_eq!(episode_file_id, 0); + } + + #[test] + #[should_panic(expected = "Season details have not been loaded")] + fn test_extract_episode_file_id_empty_season_details_modal_panics() { + let mut app = App::default(); + + let episode_file_id = SeasonDetailsHandler::with( + DEFAULT_KEYBINDINGS.esc.key, + &mut app, + ActiveSonarrBlock::SeasonDetails, + None, + ).extract_episode_file_id(); + } #[test] fn test_season_details_handler_is_not_ready_when_loading() { diff --git a/src/network/sonarr_network.rs b/src/network/sonarr_network.rs index e1ffd0a..d10b70c 100644 --- a/src/network/sonarr_network.rs +++ b/src/network/sonarr_network.rs @@ -44,7 +44,7 @@ pub enum SonarrEvent { ClearBlocklist, DeleteBlocklistItem(i64), DeleteDownload(i64), - DeleteEpisodeFile(Option), + DeleteEpisodeFile(i64), DeleteIndexer(Option), DeleteRootFolder(Option), DeleteSeries(Option), @@ -478,33 +478,16 @@ impl<'a, 'b> Network<'a, 'b> { .await } - async fn delete_sonarr_episode_file(&mut self, episode_file_id: Option) -> Result<()> { - let event = SonarrEvent::DeleteEpisodeFile(None); - let id = if let Some(ep_id) = episode_file_id { - ep_id - } else { - self - .app - .lock() - .await - .data - .sonarr_data - .season_details_modal - .as_ref() - .expect("Season details have not been loaded") - .episodes - .current_selection() - .episode_file_id - }; - - info!("Deleting Sonarr episode file for episode file with id: {id}"); + async fn delete_sonarr_episode_file(&mut self, episode_file_id: i64) -> Result<()> { + let event = SonarrEvent::DeleteEpisodeFile(episode_file_id); + info!("Deleting Sonarr episode file for episode file with id: {episode_file_id}"); let request_props = self .request_props_from( event, RequestMethod::Delete, None::<()>, - Some(format!("/{id}")), + Some(format!("/{episode_file_id}")), None, ) .await; diff --git a/src/network/sonarr_network_tests.rs b/src/network/sonarr_network_tests.rs index 3d8c53d..7853379 100644 --- a/src/network/sonarr_network_tests.rs +++ b/src/network/sonarr_network_tests.rs @@ -272,7 +272,7 @@ mod test { fn test_resource_episode_file( #[values( SonarrEvent::GetEpisodeFiles(None), - SonarrEvent::DeleteEpisodeFile(None) + SonarrEvent::DeleteEpisodeFile(0) )] event: SonarrEvent, ) { @@ -569,76 +569,23 @@ mod test { None, None, None, - SonarrEvent::DeleteEpisodeFile(None), + SonarrEvent::DeleteEpisodeFile(1), Some("/1"), None, ) .await; app_arc.lock().await.data.sonarr_data.season_details_modal = Some(SeasonDetailsModal::default()); - app_arc - .lock() - .await - .data - .sonarr_data - .season_details_modal - .as_mut() - .unwrap() - .episodes - .set_items(vec![episode()]); let mut network = Network::new(&app_arc, CancellationToken::new(), Client::new()); assert!(network - .handle_sonarr_event(SonarrEvent::DeleteEpisodeFile(None)) + .handle_sonarr_event(SonarrEvent::DeleteEpisodeFile(1)) .await .is_ok()); async_server.assert_async().await; } - #[tokio::test] - async fn test_handle_delete_sonarr_episode_file_event_uses_provided_id() { - let (async_server, app_arc, _server) = mock_servarr_api( - RequestMethod::Delete, - None, - None, - None, - SonarrEvent::DeleteEpisodeFile(None), - Some("/1"), - None, - ) - .await; - let mut network = Network::new(&app_arc, CancellationToken::new(), Client::new()); - - assert!(network - .handle_sonarr_event(SonarrEvent::DeleteEpisodeFile(Some(1))) - .await - .is_ok()); - - async_server.assert_async().await; - } - - #[tokio::test] - #[should_panic(expected = "Season details have not been loaded")] - async fn test_handle_delete_sonarr_episode_file_event_empty_season_details_modal_panics() { - let (_async_server, app_arc, _server) = mock_servarr_api( - RequestMethod::Delete, - None, - None, - None, - SonarrEvent::DeleteEpisodeFile(None), - Some("/1"), - None, - ) - .await; - let mut network = Network::new(&app_arc, CancellationToken::new(), Client::new()); - - network - .handle_sonarr_event(SonarrEvent::DeleteEpisodeFile(None)) - .await - .unwrap(); - } - #[tokio::test] async fn test_handle_delete_sonarr_download_event() { let (async_server, app_arc, _server) = mock_servarr_api(