Merge pull request #27 from Dark-Alex-17/race-condition-refactor

Race condition refactor
This commit is contained in:
Alex Clarke
2024-12-18 17:38:13 -07:00
committed by GitHub
97 changed files with 5816 additions and 6819 deletions
+3
View File
@@ -4,6 +4,9 @@ set -e
echo "Running pre-push hook:"
echo "Executing: cargo fmt"
cargo fmt
echo "Executing: make lint"
make lint
+3
View File
@@ -4,6 +4,9 @@ set -e
echo "Running pre-push hook:"
echo "Executing: cargo fmt --check"
cargo fmt --check
echo "Executing: make lint"
make lint
+42 -7
View File
@@ -77,7 +77,9 @@ impl<'a> App<'a> {
}
ActiveRadarrBlock::TestIndexer => {
self
.dispatch_network_event(RadarrEvent::TestIndexer(None).into())
.dispatch_network_event(
RadarrEvent::TestIndexer(self.extract_radarr_indexer_id().await).into(),
)
.await;
}
ActiveRadarrBlock::TestAllIndexers => {
@@ -93,7 +95,7 @@ impl<'a> App<'a> {
.dispatch_network_event(RadarrEvent::GetQueuedEvents.into())
.await;
self
.dispatch_network_event(RadarrEvent::GetLogs(None).into())
.dispatch_network_event(RadarrEvent::GetLogs(500).into())
.await;
}
ActiveRadarrBlock::SystemUpdates => {
@@ -103,17 +105,23 @@ impl<'a> App<'a> {
}
ActiveRadarrBlock::AddMovieSearchResults => {
self
.dispatch_network_event(RadarrEvent::SearchNewMovie(None).into())
.dispatch_network_event(
RadarrEvent::SearchNewMovie(self.extract_movie_search_query().await).into(),
)
.await;
}
ActiveRadarrBlock::MovieDetails | ActiveRadarrBlock::FileInfo => {
self
.dispatch_network_event(RadarrEvent::GetMovieDetails(None).into())
.dispatch_network_event(
RadarrEvent::GetMovieDetails(self.extract_movie_id().await).into(),
)
.await;
}
ActiveRadarrBlock::MovieHistory => {
self
.dispatch_network_event(RadarrEvent::GetMovieHistory(None).into())
.dispatch_network_event(
RadarrEvent::GetMovieHistory(self.extract_movie_id().await).into(),
)
.await;
}
ActiveRadarrBlock::Cast | ActiveRadarrBlock::Crew => {
@@ -123,7 +131,9 @@ impl<'a> App<'a> {
|| movie_details_modal.movie_crew.items.is_empty() =>
{
self
.dispatch_network_event(RadarrEvent::GetMovieCredits(None).into())
.dispatch_network_event(
RadarrEvent::GetMovieCredits(self.extract_movie_id().await).into(),
)
.await;
}
_ => (),
@@ -132,7 +142,7 @@ impl<'a> App<'a> {
ActiveRadarrBlock::ManualSearch => match self.data.radarr_data.movie_details_modal.as_ref() {
Some(movie_details_modal) if movie_details_modal.movie_releases.items.is_empty() => {
self
.dispatch_network_event(RadarrEvent::GetReleases(None).into())
.dispatch_network_event(RadarrEvent::GetReleases(self.extract_movie_id().await).into())
.await;
}
_ => (),
@@ -219,4 +229,29 @@ impl<'a> App<'a> {
.collection_movies
.set_items(collection_movies);
}
async fn extract_movie_id(&self) -> i64 {
self.data.radarr_data.movies.current_selection().clone().id
}
async fn extract_movie_search_query(&self) -> String {
self
.data
.radarr_data
.add_movie_search
.as_ref()
.expect("Add movie search is empty")
.text
.clone()
}
async fn extract_radarr_indexer_id(&self) -> i64 {
self
.data
.radarr_data
.indexers
.current_selection()
.clone()
.id
}
}
File diff suppressed because it is too large Load Diff
+78 -14
View File
@@ -38,30 +38,42 @@ impl<'a> App<'a> {
}
ActiveSonarrBlock::SeriesHistory => {
self
.dispatch_network_event(SonarrEvent::GetSeriesHistory(None).into())
.dispatch_network_event(
SonarrEvent::GetSeriesHistory(self.extract_series_id().await).into(),
)
.await;
}
ActiveSonarrBlock::SeasonDetails => {
self
.dispatch_network_event(SonarrEvent::GetEpisodes(None).into())
.dispatch_network_event(SonarrEvent::GetEpisodes(self.extract_series_id().await).into())
.await;
self
.dispatch_network_event(SonarrEvent::GetEpisodeFiles(None).into())
.dispatch_network_event(
SonarrEvent::GetEpisodeFiles(self.extract_series_id().await).into(),
)
.await;
self
.dispatch_network_event(SonarrEvent::GetDownloads.into())
.await;
}
ActiveSonarrBlock::SeasonHistory => {
self
.dispatch_network_event(SonarrEvent::GetSeasonHistory(None).into())
.await;
if !self.data.sonarr_data.seasons.is_empty() {
self
.dispatch_network_event(
SonarrEvent::GetSeasonHistory(self.extract_series_id_season_number_tuple().await)
.into(),
)
.await;
}
}
ActiveSonarrBlock::ManualSeasonSearch => {
match self.data.sonarr_data.season_details_modal.as_ref() {
Some(season_details_modal) if season_details_modal.season_releases.is_empty() => {
self
.dispatch_network_event(SonarrEvent::GetSeasonReleases(None).into())
.dispatch_network_event(
SonarrEvent::GetSeasonReleases(self.extract_series_id_season_number_tuple().await)
.into(),
)
.await;
}
_ => (),
@@ -69,12 +81,16 @@ impl<'a> App<'a> {
}
ActiveSonarrBlock::EpisodeDetails | ActiveSonarrBlock::EpisodeFile => {
self
.dispatch_network_event(SonarrEvent::GetEpisodeDetails(None).into())
.dispatch_network_event(
SonarrEvent::GetEpisodeDetails(self.extract_episode_id().await).into(),
)
.await;
}
ActiveSonarrBlock::EpisodeHistory => {
self
.dispatch_network_event(SonarrEvent::GetEpisodeHistory(None).into())
.dispatch_network_event(
SonarrEvent::GetEpisodeHistory(self.extract_episode_id().await).into(),
)
.await;
}
ActiveSonarrBlock::ManualEpisodeSearch => {
@@ -82,7 +98,9 @@ impl<'a> App<'a> {
if let Some(episode_details_modal) = season_details_modal.episode_details_modal.as_ref() {
if episode_details_modal.episode_releases.is_empty() {
self
.dispatch_network_event(SonarrEvent::GetEpisodeReleases(None).into())
.dispatch_network_event(
SonarrEvent::GetEpisodeReleases(self.extract_episode_id().await).into(),
)
.await;
}
}
@@ -103,7 +121,7 @@ impl<'a> App<'a> {
}
ActiveSonarrBlock::History => {
self
.dispatch_network_event(SonarrEvent::GetHistory(None).into())
.dispatch_network_event(SonarrEvent::GetHistory(500).into())
.await;
}
ActiveSonarrBlock::RootFolders => {
@@ -126,7 +144,9 @@ impl<'a> App<'a> {
}
ActiveSonarrBlock::TestIndexer => {
self
.dispatch_network_event(SonarrEvent::TestIndexer(None).into())
.dispatch_network_event(
SonarrEvent::TestIndexer(self.extract_sonarr_indexer_id().await).into(),
)
.await;
}
ActiveSonarrBlock::TestAllIndexers => {
@@ -142,12 +162,14 @@ impl<'a> App<'a> {
.dispatch_network_event(SonarrEvent::GetQueuedEvents.into())
.await;
self
.dispatch_network_event(SonarrEvent::GetLogs(None).into())
.dispatch_network_event(SonarrEvent::GetLogs(500).into())
.await;
}
ActiveSonarrBlock::AddSeriesSearchResults => {
self
.dispatch_network_event(SonarrEvent::SearchNewSeries(None).into())
.dispatch_network_event(
SonarrEvent::SearchNewSeries(self.extract_add_new_series_search_query().await).into(),
)
.await;
}
ActiveSonarrBlock::SystemUpdates => {
@@ -242,4 +264,46 @@ impl<'a> App<'a> {
.collect();
self.data.sonarr_data.seasons.set_items(seasons);
}
async fn extract_episode_id(&self) -> i64 {
self
.data
.sonarr_data
.season_details_modal
.as_ref()
.expect("Season details have not been loaded")
.episodes
.current_selection()
.id
}
async fn extract_series_id(&self) -> i64 {
self.data.sonarr_data.series.current_selection().id
}
async fn extract_series_id_season_number_tuple(&self) -> (i64, i64) {
let series_id = self.data.sonarr_data.series.current_selection().id;
let season_number = self
.data
.sonarr_data
.seasons
.current_selection()
.season_number;
(series_id, season_number)
}
async fn extract_add_new_series_search_query(&self) -> String {
self
.data
.sonarr_data
.add_series_search
.as_ref()
.expect("Add series search is empty")
.text
.clone()
}
async fn extract_sonarr_indexer_id(&self) -> i64 {
self.data.sonarr_data.indexers.current_selection().id
}
}
+152 -14
View File
@@ -4,6 +4,9 @@ mod tests {
use pretty_assertions::{assert_eq, assert_str_eq};
use tokio::sync::mpsc;
use crate::models::servarr_data::sonarr::sonarr_data::sonarr_test_utils::utils::create_test_sonarr_data;
use crate::models::servarr_models::Indexer;
use crate::models::sonarr_models::Episode;
use crate::{
app::App,
models::{
@@ -40,6 +43,10 @@ mod tests {
#[tokio::test]
async fn test_dispatch_by_series_history_block() {
let (mut app, mut sync_network_rx) = construct_app_unit();
app.data.sonarr_data.series.set_items(vec![Series {
id: 1,
..Series::default()
}]);
app
.dispatch_by_sonarr_block(&ActiveSonarrBlock::SeriesHistory)
@@ -48,7 +55,7 @@ mod tests {
assert!(app.is_loading);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetSeriesHistory(None).into()
SonarrEvent::GetSeriesHistory(1).into()
);
assert!(!app.data.sonarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
@@ -80,6 +87,10 @@ mod tests {
#[tokio::test]
async fn test_dispatch_by_season_details_block() {
let (mut app, mut sync_network_rx) = construct_app_unit();
app.data.sonarr_data.series.set_items(vec![Series {
id: 1,
..Series::default()
}]);
app
.dispatch_by_sonarr_block(&ActiveSonarrBlock::SeasonDetails)
@@ -88,11 +99,11 @@ mod tests {
assert!(app.is_loading);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetEpisodes(None).into()
SonarrEvent::GetEpisodes(1).into()
);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetEpisodeFiles(None).into()
SonarrEvent::GetEpisodeFiles(1).into()
);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
@@ -105,6 +116,14 @@ mod tests {
#[tokio::test]
async fn test_dispatch_by_season_history_block() {
let (mut app, mut sync_network_rx) = construct_app_unit();
app.data.sonarr_data.series.set_items(vec![Series {
id: 1,
..Series::default()
}]);
app.data.sonarr_data.seasons.set_items(vec![Season {
season_number: 1,
..Season::default()
}]);
app
.dispatch_by_sonarr_block(&ActiveSonarrBlock::SeasonHistory)
@@ -113,16 +132,41 @@ mod tests {
assert!(app.is_loading);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetSeasonHistory(None).into()
SonarrEvent::GetSeasonHistory((1, 1)).into()
);
assert!(!app.data.sonarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
}
#[tokio::test]
async fn test_dispatch_by_season_history_block_no_op_when_seasons_table_is_empty() {
let (mut app, _) = construct_app_unit();
app.data.sonarr_data.series.set_items(vec![Series {
id: 1,
..Series::default()
}]);
app
.dispatch_by_sonarr_block(&ActiveSonarrBlock::SeasonHistory)
.await;
assert!(!app.is_loading);
assert!(!app.data.sonarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
}
#[tokio::test]
async fn test_dispatch_by_manual_season_search_block() {
let (mut app, mut sync_network_rx) = construct_app_unit();
app.data.sonarr_data.season_details_modal = Some(SeasonDetailsModal::default());
app.data.sonarr_data.series.set_items(vec![Series {
id: 1,
..Series::default()
}]);
app.data.sonarr_data.seasons.set_items(vec![Season {
season_number: 1,
..Season::default()
}]);
app
.dispatch_by_sonarr_block(&ActiveSonarrBlock::ManualSeasonSearch)
@@ -131,7 +175,7 @@ mod tests {
assert!(app.is_loading);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetSeasonReleases(None).into()
SonarrEvent::GetSeasonReleases((1, 1)).into()
);
assert!(!app.data.sonarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
@@ -174,6 +218,7 @@ mod tests {
#[tokio::test]
async fn test_dispatch_by_episode_details_block() {
let (mut app, mut sync_network_rx) = construct_app_unit();
app.data.sonarr_data = create_test_sonarr_data();
app
.dispatch_by_sonarr_block(&ActiveSonarrBlock::EpisodeDetails)
@@ -182,7 +227,7 @@ mod tests {
assert!(app.is_loading);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetEpisodeDetails(None).into()
SonarrEvent::GetEpisodeDetails(0).into()
);
assert!(!app.data.sonarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
@@ -191,6 +236,7 @@ mod tests {
#[tokio::test]
async fn test_dispatch_by_episode_file_block() {
let (mut app, mut sync_network_rx) = construct_app_unit();
app.data.sonarr_data = create_test_sonarr_data();
app
.dispatch_by_sonarr_block(&ActiveSonarrBlock::EpisodeFile)
@@ -199,7 +245,7 @@ mod tests {
assert!(app.is_loading);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetEpisodeDetails(None).into()
SonarrEvent::GetEpisodeDetails(0).into()
);
assert!(!app.data.sonarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
@@ -208,6 +254,12 @@ mod tests {
#[tokio::test]
async fn test_dispatch_by_episode_history_block() {
let (mut app, mut sync_network_rx) = construct_app_unit();
let mut season_details_modal = SeasonDetailsModal::default();
season_details_modal.episodes.set_items(vec![Episode {
id: 1,
..Episode::default()
}]);
app.data.sonarr_data.season_details_modal = Some(season_details_modal);
app
.dispatch_by_sonarr_block(&ActiveSonarrBlock::EpisodeHistory)
@@ -216,7 +268,7 @@ mod tests {
assert!(app.is_loading);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetEpisodeHistory(None).into()
SonarrEvent::GetEpisodeHistory(1).into()
);
assert!(!app.data.sonarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
@@ -225,10 +277,14 @@ mod tests {
#[tokio::test]
async fn test_dispatch_by_manual_episode_search_block() {
let (mut app, mut sync_network_rx) = construct_app_unit();
let season_details_modal = SeasonDetailsModal {
let mut season_details_modal = SeasonDetailsModal {
episode_details_modal: Some(EpisodeDetailsModal::default()),
..SeasonDetailsModal::default()
};
season_details_modal.episodes.set_items(vec![Episode {
id: 1,
..Episode::default()
}]);
app.data.sonarr_data.season_details_modal = Some(season_details_modal);
app
@@ -238,7 +294,7 @@ mod tests {
assert!(app.is_loading);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetEpisodeReleases(None).into()
SonarrEvent::GetEpisodeReleases(1).into()
);
assert!(!app.data.sonarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
@@ -293,7 +349,7 @@ mod tests {
assert!(app.is_loading);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetHistory(None).into()
SonarrEvent::GetHistory(500).into()
);
assert!(!app.data.sonarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
@@ -403,6 +459,10 @@ mod tests {
#[tokio::test]
async fn test_dispatch_by_test_indexer_block() {
let (mut app, mut sync_network_rx) = construct_app_unit();
app.data.sonarr_data.indexers.set_items(vec![Indexer {
id: 1,
..Indexer::default()
}]);
app
.dispatch_by_sonarr_block(&ActiveSonarrBlock::TestIndexer)
@@ -411,7 +471,7 @@ mod tests {
assert!(app.is_loading);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::TestIndexer(None).into()
SonarrEvent::TestIndexer(1).into()
);
assert_eq!(app.tick_count, 0);
}
@@ -451,7 +511,7 @@ mod tests {
);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::GetLogs(None).into()
SonarrEvent::GetLogs(500).into()
);
assert!(!app.data.sonarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
@@ -477,6 +537,7 @@ mod tests {
#[tokio::test]
async fn test_dispatch_by_add_series_search_results_block() {
let (mut app, mut sync_network_rx) = construct_app_unit();
app.data.sonarr_data.add_series_search = Some("test search".into());
app
.dispatch_by_sonarr_block(&ActiveSonarrBlock::AddSeriesSearchResults)
@@ -485,7 +546,7 @@ mod tests {
assert!(app.is_loading);
assert_eq!(
sync_network_rx.recv().await.unwrap(),
SonarrEvent::SearchNewSeries(None).into()
SonarrEvent::SearchNewSeries("test search".into()).into()
);
assert!(!app.data.sonarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
@@ -727,6 +788,83 @@ mod tests {
);
}
#[tokio::test]
async 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 {
id: 1,
..Episode::default()
}]);
app.data.sonarr_data.season_details_modal = Some(season_details_modal);
assert_eq!(app.extract_episode_id().await, 1);
}
#[tokio::test]
#[should_panic(expected = "Season details have not been loaded")]
async fn test_extract_episode_id_requires_season_details_modal_to_be_some() {
let app = App::default();
assert_eq!(app.extract_episode_id().await, 0);
}
#[tokio::test]
async fn test_extract_series_id() {
let mut app = App::default();
app.data.sonarr_data.series.set_items(vec![Series {
id: 1,
..Series::default()
}]);
assert_eq!(app.extract_series_id().await, 1);
}
#[tokio::test]
async fn test_extract_series_id_season_number_tuple() {
let mut app = App::default();
app.data.sonarr_data.series.set_items(vec![Series {
id: 1,
..Series::default()
}]);
app.data.sonarr_data.seasons.set_items(vec![Season {
season_number: 1,
..Season::default()
}]);
assert_eq!(app.extract_series_id_season_number_tuple().await, (1, 1));
}
#[tokio::test]
async fn test_extract_add_new_series_search_query() {
let mut app = App::default();
app.data.sonarr_data.add_series_search = Some("test search".into());
assert_str_eq!(
app.extract_add_new_series_search_query().await,
"test search"
);
}
#[tokio::test]
#[should_panic(expected = "Add series search is empty")]
async fn test_extract_add_new_series_search_query_panics_when_the_query_is_not_set() {
let app = App::default();
app.extract_add_new_series_search_query().await;
}
#[tokio::test]
async fn test_extract_sonarr_indexer_id() {
let mut app = App::default();
app.data.sonarr_data.indexers.set_items(vec![Indexer {
id: 1,
..Indexer::default()
}]);
assert_eq!(app.extract_sonarr_indexer_id().await, 1);
}
fn construct_app_unit<'a>() -> (App<'a>, mpsc::Receiver<NetworkEvent>) {
let (sync_network_tx, sync_network_rx) = mpsc::channel::<NetworkEvent>(500);
let mut app = App {
+8 -4
View File
@@ -4,6 +4,8 @@ use anyhow::Result;
use clap::{arg, command, ArgAction, Subcommand};
use tokio::sync::Mutex;
use super::RadarrCommand;
use crate::models::servarr_models::AddRootFolderBody;
use crate::{
app::App,
cli::{CliCommandHandler, Command},
@@ -11,8 +13,6 @@ use crate::{
network::{radarr_network::RadarrEvent, NetworkTrait},
};
use super::RadarrCommand;
#[cfg(test)]
#[path = "add_command_handler_tests.rs"]
mod add_command_handler_tests;
@@ -125,6 +125,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, RadarrAddCommand> for RadarrAddCommandHan
minimum_availability: minimum_availability.to_string(),
monitored: !disable_monitoring,
tags,
tag_input_string: None,
add_options: AddMovieOptions {
monitor: monitor.to_string(),
search_for_movie: !no_search_for_movie,
@@ -132,14 +133,17 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, RadarrAddCommand> for RadarrAddCommandHan
};
let resp = self
.network
.handle_network_event(RadarrEvent::AddMovie(Some(body)).into())
.handle_network_event(RadarrEvent::AddMovie(body).into())
.await?;
serde_json::to_string_pretty(&resp)?
}
RadarrAddCommand::RootFolder { root_folder_path } => {
let add_root_folder_body = AddRootFolderBody {
path: root_folder_path,
};
let resp = self
.network
.handle_network_event(RadarrEvent::AddRootFolder(Some(root_folder_path)).into())
.handle_network_event(RadarrEvent::AddRootFolder(add_root_folder_body).into())
.await?;
serde_json::to_string_pretty(&resp)?
}
+7 -2
View File
@@ -368,6 +368,7 @@ mod tests {
use super::*;
use mockall::predicate::eq;
use crate::models::servarr_models::AddRootFolderBody;
use serde_json::json;
use tokio::sync::Mutex;
@@ -381,6 +382,7 @@ mod tests {
minimum_availability: "released".to_owned(),
monitored: false,
tags: vec![1, 2],
tag_input_string: None,
add_options: AddMovieOptions {
monitor: "movieAndCollection".to_owned(),
search_for_movie: false,
@@ -390,7 +392,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
RadarrEvent::AddMovie(Some(expected_add_movie_body)).into(),
RadarrEvent::AddMovie(expected_add_movie_body).into(),
))
.times(1)
.returning(|_| {
@@ -420,11 +422,14 @@ mod tests {
#[tokio::test]
async fn test_handle_add_root_folder_command() {
let expected_root_folder_path = "/nfs/test".to_owned();
let expected_add_root_folder_body = AddRootFolderBody {
path: expected_root_folder_path.clone(),
};
let mut mock_network = MockNetworkTrait::new();
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
RadarrEvent::AddRootFolder(Some(expected_root_folder_path.clone())).into(),
RadarrEvent::AddRootFolder(expected_add_root_folder_body).into(),
))
.times(1)
.returning(|_| {
+5 -5
View File
@@ -89,21 +89,21 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, RadarrDeleteCommand> for RadarrDeleteComm
RadarrDeleteCommand::BlocklistItem { blocklist_item_id } => {
let resp = self
.network
.handle_network_event(RadarrEvent::DeleteBlocklistItem(Some(blocklist_item_id)).into())
.handle_network_event(RadarrEvent::DeleteBlocklistItem(blocklist_item_id).into())
.await?;
serde_json::to_string_pretty(&resp)?
}
RadarrDeleteCommand::Download { download_id } => {
let resp = self
.network
.handle_network_event(RadarrEvent::DeleteDownload(Some(download_id)).into())
.handle_network_event(RadarrEvent::DeleteDownload(download_id).into())
.await?;
serde_json::to_string_pretty(&resp)?
}
RadarrDeleteCommand::Indexer { indexer_id } => {
let resp = self
.network
.handle_network_event(RadarrEvent::DeleteIndexer(Some(indexer_id)).into())
.handle_network_event(RadarrEvent::DeleteIndexer(indexer_id).into())
.await?;
serde_json::to_string_pretty(&resp)?
}
@@ -119,14 +119,14 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, RadarrDeleteCommand> for RadarrDeleteComm
};
let resp = self
.network
.handle_network_event(RadarrEvent::DeleteMovie(Some(delete_movie_params)).into())
.handle_network_event(RadarrEvent::DeleteMovie(delete_movie_params).into())
.await?;
serde_json::to_string_pretty(&resp)?
}
RadarrDeleteCommand::RootFolder { root_folder_id } => {
let resp = self
.network
.handle_network_event(RadarrEvent::DeleteRootFolder(Some(root_folder_id)).into())
.handle_network_event(RadarrEvent::DeleteRootFolder(root_folder_id).into())
.await?;
serde_json::to_string_pretty(&resp)?
}
@@ -268,7 +268,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
RadarrEvent::DeleteBlocklistItem(Some(expected_blocklist_item_id)).into(),
RadarrEvent::DeleteBlocklistItem(expected_blocklist_item_id).into(),
))
.times(1)
.returning(|_| {
@@ -299,7 +299,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
RadarrEvent::DeleteDownload(Some(expected_download_id)).into(),
RadarrEvent::DeleteDownload(expected_download_id).into(),
))
.times(1)
.returning(|_| {
@@ -325,7 +325,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
RadarrEvent::DeleteIndexer(Some(expected_indexer_id)).into(),
RadarrEvent::DeleteIndexer(expected_indexer_id).into(),
))
.times(1)
.returning(|_| {
@@ -355,7 +355,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
RadarrEvent::DeleteMovie(Some(expected_delete_movie_params)).into(),
RadarrEvent::DeleteMovie(expected_delete_movie_params).into(),
))
.times(1)
.returning(|_| {
@@ -385,7 +385,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
RadarrEvent::DeleteRootFolder(Some(expected_root_folder_id)).into(),
RadarrEvent::DeleteRootFolder(expected_root_folder_id).into(),
))
.times(1)
.returning(|_| {
+6 -4
View File
@@ -390,7 +390,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, RadarrEditCommand> for RadarrEditCommandH
};
self
.network
.handle_network_event(RadarrEvent::EditAllIndexerSettings(Some(params)).into())
.handle_network_event(RadarrEvent::EditAllIndexerSettings(params).into())
.await?;
"All indexer settings updated".to_owned()
} else {
@@ -420,7 +420,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, RadarrEditCommand> for RadarrEditCommandH
};
self
.network
.handle_network_event(RadarrEvent::EditCollection(Some(edit_collection_params)).into())
.handle_network_event(RadarrEvent::EditCollection(edit_collection_params).into())
.await?;
"Collection updated".to_owned()
}
@@ -455,13 +455,14 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, RadarrEditCommand> for RadarrEditCommandH
api_key,
seed_ratio,
tags: tag,
tag_input_string: None,
priority,
clear_tags,
};
self
.network
.handle_network_event(RadarrEvent::EditIndexer(Some(edit_indexer_params)).into())
.handle_network_event(RadarrEvent::EditIndexer(edit_indexer_params).into())
.await?;
"Indexer updated".to_owned()
}
@@ -483,12 +484,13 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, RadarrEditCommand> for RadarrEditCommandH
quality_profile_id,
root_folder_path,
tags: tag,
tag_input_string: None,
clear_tags,
};
self
.network
.handle_network_event(RadarrEvent::EditMovie(Some(edit_movie_params)).into())
.handle_network_event(RadarrEvent::EditMovie(edit_movie_params).into())
.await?;
"Movie Updated".to_owned()
}
+18 -12
View File
@@ -857,7 +857,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
RadarrEvent::EditAllIndexerSettings(Some(expected_edit_all_indexer_settings)).into(),
RadarrEvent::EditAllIndexerSettings(expected_edit_all_indexer_settings).into(),
))
.times(1)
.returning(|_| {
@@ -928,7 +928,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
RadarrEvent::EditAllIndexerSettings(Some(expected_edit_all_indexer_settings)).into(),
RadarrEvent::EditAllIndexerSettings(expected_edit_all_indexer_settings).into(),
))
.times(1)
.returning(|_| {
@@ -1000,7 +1000,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
RadarrEvent::EditAllIndexerSettings(Some(expected_edit_all_indexer_settings)).into(),
RadarrEvent::EditAllIndexerSettings(expected_edit_all_indexer_settings).into(),
))
.times(1)
.returning(|_| {
@@ -1047,7 +1047,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
RadarrEvent::EditCollection(Some(expected_edit_collection_params)).into(),
RadarrEvent::EditCollection(expected_edit_collection_params).into(),
))
.times(1)
.returning(|_| {
@@ -1089,7 +1089,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
RadarrEvent::EditCollection(Some(expected_edit_collection_params)).into(),
RadarrEvent::EditCollection(expected_edit_collection_params).into(),
))
.times(1)
.returning(|_| {
@@ -1131,7 +1131,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
RadarrEvent::EditCollection(Some(expected_edit_collection_params)).into(),
RadarrEvent::EditCollection(expected_edit_collection_params).into(),
))
.times(1)
.returning(|_| {
@@ -1171,6 +1171,7 @@ mod tests {
api_key: Some("testKey".to_owned()),
seed_ratio: Some("1.2".to_owned()),
tags: Some(vec![1, 2]),
tag_input_string: None,
priority: Some(25),
clear_tags: false,
};
@@ -1178,7 +1179,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
RadarrEvent::EditIndexer(Some(expected_edit_indexer_params)).into(),
RadarrEvent::EditIndexer(expected_edit_indexer_params).into(),
))
.times(1)
.returning(|_| {
@@ -1224,6 +1225,7 @@ mod tests {
api_key: Some("testKey".to_owned()),
seed_ratio: Some("1.2".to_owned()),
tags: Some(vec![1, 2]),
tag_input_string: None,
priority: Some(25),
clear_tags: false,
};
@@ -1231,7 +1233,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
RadarrEvent::EditIndexer(Some(expected_edit_indexer_params)).into(),
RadarrEvent::EditIndexer(expected_edit_indexer_params).into(),
))
.times(1)
.returning(|_| {
@@ -1277,6 +1279,7 @@ mod tests {
api_key: Some("testKey".to_owned()),
seed_ratio: Some("1.2".to_owned()),
tags: Some(vec![1, 2]),
tag_input_string: None,
priority: Some(25),
clear_tags: false,
};
@@ -1284,7 +1287,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
RadarrEvent::EditIndexer(Some(expected_edit_indexer_params)).into(),
RadarrEvent::EditIndexer(expected_edit_indexer_params).into(),
))
.times(1)
.returning(|_| {
@@ -1327,13 +1330,14 @@ mod tests {
quality_profile_id: Some(1),
root_folder_path: Some("/nfs/test".to_owned()),
tags: Some(vec![1, 2]),
tag_input_string: None,
clear_tags: false,
};
let mut mock_network = MockNetworkTrait::new();
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
RadarrEvent::EditMovie(Some(expected_edit_movie_params)).into(),
RadarrEvent::EditMovie(expected_edit_movie_params).into(),
))
.times(1)
.returning(|_| {
@@ -1369,13 +1373,14 @@ mod tests {
quality_profile_id: Some(1),
root_folder_path: Some("/nfs/test".to_owned()),
tags: Some(vec![1, 2]),
tag_input_string: None,
clear_tags: false,
};
let mut mock_network = MockNetworkTrait::new();
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
RadarrEvent::EditMovie(Some(expected_edit_movie_params)).into(),
RadarrEvent::EditMovie(expected_edit_movie_params).into(),
))
.times(1)
.returning(|_| {
@@ -1411,13 +1416,14 @@ mod tests {
quality_profile_id: Some(1),
root_folder_path: Some("/nfs/test".to_owned()),
tags: Some(vec![1, 2]),
tag_input_string: None,
clear_tags: false,
};
let mut mock_network = MockNetworkTrait::new();
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
RadarrEvent::EditMovie(Some(expected_edit_movie_params)).into(),
RadarrEvent::EditMovie(expected_edit_movie_params).into(),
))
.times(1)
.returning(|_| {
+2 -2
View File
@@ -90,14 +90,14 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, RadarrGetCommand> for RadarrGetCommandHan
RadarrGetCommand::MovieDetails { movie_id } => {
let resp = self
.network
.handle_network_event(RadarrEvent::GetMovieDetails(Some(movie_id)).into())
.handle_network_event(RadarrEvent::GetMovieDetails(movie_id).into())
.await?;
serde_json::to_string_pretty(&resp)?
}
RadarrGetCommand::MovieHistory { movie_id } => {
let resp = self
.network
.handle_network_event(RadarrEvent::GetMovieHistory(Some(movie_id)).into())
.handle_network_event(RadarrEvent::GetMovieHistory(movie_id).into())
.await?;
serde_json::to_string_pretty(&resp)?
}
+2 -2
View File
@@ -182,7 +182,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
RadarrEvent::GetMovieDetails(Some(expected_movie_id)).into(),
RadarrEvent::GetMovieDetails(expected_movie_id).into(),
))
.times(1)
.returning(|_| {
@@ -208,7 +208,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
RadarrEvent::GetMovieHistory(Some(expected_movie_id)).into(),
RadarrEvent::GetMovieHistory(expected_movie_id).into(),
))
.times(1)
.returning(|_| {
+2 -2
View File
@@ -131,7 +131,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, RadarrListCommand> for RadarrListCommandH
} => {
let logs = self
.network
.handle_network_event(RadarrEvent::GetLogs(Some(events)).into())
.handle_network_event(RadarrEvent::GetLogs(events).into())
.await?;
if output_in_log_format {
@@ -152,7 +152,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, RadarrListCommand> for RadarrListCommandH
RadarrListCommand::MovieCredits { movie_id } => {
let resp = self
.network
.handle_network_event(RadarrEvent::GetMovieCredits(Some(movie_id)).into())
.handle_network_event(RadarrEvent::GetMovieCredits(movie_id).into())
.await?;
serde_json::to_string_pretty(&resp)?
}
+2 -2
View File
@@ -163,7 +163,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
RadarrEvent::GetMovieCredits(Some(expected_movie_id)).into(),
RadarrEvent::GetMovieCredits(expected_movie_id).into(),
))
.times(1)
.returning(|_| {
@@ -189,7 +189,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
RadarrEvent::GetLogs(Some(expected_events)).into(),
RadarrEvent::GetLogs(expected_events).into(),
))
.times(1)
.returning(|_| {
+6 -6
View File
@@ -209,7 +209,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, RadarrCommand> for RadarrCliHandler<'a, '
};
let resp = self
.network
.handle_network_event(RadarrEvent::DownloadRelease(Some(params)).into())
.handle_network_event(RadarrEvent::DownloadRelease(params).into())
.await?;
serde_json::to_string_pretty(&resp)?
}
@@ -217,28 +217,28 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, RadarrCommand> for RadarrCliHandler<'a, '
println!("Searching for releases. This may take a minute...");
let resp = self
.network
.handle_network_event(RadarrEvent::GetReleases(Some(movie_id)).into())
.handle_network_event(RadarrEvent::GetReleases(movie_id).into())
.await?;
serde_json::to_string_pretty(&resp)?
}
RadarrCommand::SearchNewMovie { query } => {
let resp = self
.network
.handle_network_event(RadarrEvent::SearchNewMovie(Some(query)).into())
.handle_network_event(RadarrEvent::SearchNewMovie(query).into())
.await?;
serde_json::to_string_pretty(&resp)?
}
RadarrCommand::StartTask { task_name } => {
let resp = self
.network
.handle_network_event(RadarrEvent::StartTask(Some(task_name)).into())
.handle_network_event(RadarrEvent::StartTask(task_name).into())
.await?;
serde_json::to_string_pretty(&resp)?
}
RadarrCommand::TestIndexer { indexer_id } => {
let resp = self
.network
.handle_network_event(RadarrEvent::TestIndexer(Some(indexer_id)).into())
.handle_network_event(RadarrEvent::TestIndexer(indexer_id).into())
.await?;
serde_json::to_string_pretty(&resp)?
}
@@ -253,7 +253,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, RadarrCommand> for RadarrCliHandler<'a, '
RadarrCommand::TriggerAutomaticSearch { movie_id } => {
let resp = self
.network
.handle_network_event(RadarrEvent::TriggerAutomaticSearch(Some(movie_id)).into())
.handle_network_event(RadarrEvent::TriggerAutomaticSearch(movie_id).into())
.await?;
serde_json::to_string_pretty(&resp)?
}
+12 -12
View File
@@ -293,9 +293,9 @@ mod tests {
)))
});
let app_arc = Arc::new(Mutex::new(App::default()));
let claer_blocklist_command = RadarrCommand::ClearBlocklist;
let clear_blocklist_command = RadarrCommand::ClearBlocklist;
let result = RadarrCliHandler::with(&app_arc, claer_blocklist_command, &mut mock_network)
let result = RadarrCliHandler::with(&app_arc, clear_blocklist_command, &mut mock_network)
.handle()
.await;
@@ -313,7 +313,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
RadarrEvent::DownloadRelease(Some(expected_release_download_body)).into(),
RadarrEvent::DownloadRelease(expected_release_download_body).into(),
))
.times(1)
.returning(|_| {
@@ -342,7 +342,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
RadarrEvent::GetReleases(Some(expected_movie_id)).into(),
RadarrEvent::GetReleases(expected_movie_id).into(),
))
.times(1)
.returning(|_| {
@@ -367,7 +367,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
RadarrEvent::SearchNewMovie(Some(expected_search_query)).into(),
RadarrEvent::SearchNewMovie(expected_search_query).into(),
))
.times(1)
.returning(|_| {
@@ -394,7 +394,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
RadarrEvent::StartTask(Some(expected_task_name)).into(),
RadarrEvent::StartTask(expected_task_name).into(),
))
.times(1)
.returning(|_| {
@@ -421,7 +421,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
RadarrEvent::TestIndexer(Some(expected_indexer_id)).into(),
RadarrEvent::TestIndexer(expected_indexer_id).into(),
))
.times(1)
.returning(|_| {
@@ -468,7 +468,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
RadarrEvent::TriggerAutomaticSearch(Some(expected_movie_id)).into(),
RadarrEvent::TriggerAutomaticSearch(expected_movie_id).into(),
))
.times(1)
.returning(|_| {
@@ -524,7 +524,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
RadarrEvent::DeleteBlocklistItem(Some(expected_blocklist_item_id)).into(),
RadarrEvent::DeleteBlocklistItem(expected_blocklist_item_id).into(),
))
.times(1)
.returning(|_| {
@@ -584,7 +584,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
RadarrEvent::EditAllIndexerSettings(Some(expected_edit_all_indexer_settings)).into(),
RadarrEvent::EditAllIndexerSettings(expected_edit_all_indexer_settings).into(),
))
.times(1)
.returning(|_| {
@@ -654,7 +654,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
RadarrEvent::GetMovieCredits(Some(expected_movie_id)).into(),
RadarrEvent::GetMovieCredits(expected_movie_id).into(),
))
.times(1)
.returning(|_| {
@@ -680,7 +680,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
RadarrEvent::UpdateAndScan(Some(expected_movie_id)).into(),
RadarrEvent::UpdateAndScan(expected_movie_id).into(),
))
.times(1)
.returning(|_| {
+1 -1
View File
@@ -88,7 +88,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, RadarrRefreshCommand>
RadarrRefreshCommand::Movie { movie_id } => {
let resp = self
.network
.handle_network_event(RadarrEvent::UpdateAndScan(Some(movie_id)).into())
.handle_network_event(RadarrEvent::UpdateAndScan(movie_id).into())
.await?;
serde_json::to_string_pretty(&resp)?
}
@@ -112,7 +112,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
RadarrEvent::UpdateAndScan(Some(expected_movie_id)).into(),
RadarrEvent::UpdateAndScan(expected_movie_id).into(),
))
.times(1)
.returning(|_| {
+8 -4
View File
@@ -4,6 +4,8 @@ use anyhow::Result;
use clap::{ArgAction, Subcommand};
use tokio::sync::Mutex;
use super::SonarrCommand;
use crate::models::servarr_models::AddRootFolderBody;
use crate::{
app::App,
cli::{CliCommandHandler, Command},
@@ -11,8 +13,6 @@ use crate::{
network::{sonarr_network::SonarrEvent, NetworkTrait},
};
use super::SonarrCommand;
#[cfg(test)]
#[path = "add_command_handler_tests.rs"]
mod add_command_handler_tests;
@@ -140,6 +140,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrAddCommand> for SonarrAddCommandHan
series_type: series_type.to_string(),
season_folder: !disable_season_folders,
tags,
tag_input_string: None,
add_options: AddSeriesOptions {
monitor: monitor.to_string(),
search_for_cutoff_unmet_episodes: !no_search_for_series,
@@ -148,14 +149,17 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrAddCommand> for SonarrAddCommandHan
};
let resp = self
.network
.handle_network_event(SonarrEvent::AddSeries(Some(body)).into())
.handle_network_event(SonarrEvent::AddSeries(body).into())
.await?;
serde_json::to_string_pretty(&resp)?
}
SonarrAddCommand::RootFolder { root_folder_path } => {
let add_root_folder_body = AddRootFolderBody {
path: root_folder_path,
};
let resp = self
.network
.handle_network_event(SonarrEvent::AddRootFolder(Some(root_folder_path)).into())
.handle_network_event(SonarrEvent::AddRootFolder(add_root_folder_body).into())
.await?;
serde_json::to_string_pretty(&resp)?
}
+7 -2
View File
@@ -469,17 +469,21 @@ mod tests {
use super::*;
use mockall::predicate::eq;
use crate::models::servarr_models::AddRootFolderBody;
use serde_json::json;
use tokio::sync::Mutex;
#[tokio::test]
async fn test_handle_add_root_folder_command() {
let expected_root_folder_path = "/nfs/test".to_owned();
let expected_add_root_folder_body = AddRootFolderBody {
path: expected_root_folder_path.clone(),
};
let mut mock_network = MockNetworkTrait::new();
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
SonarrEvent::AddRootFolder(Some(expected_root_folder_path.clone())).into(),
SonarrEvent::AddRootFolder(expected_add_root_folder_body.clone()).into(),
))
.times(1)
.returning(|_| {
@@ -511,6 +515,7 @@ mod tests {
series_type: "anime".to_owned(),
monitored: false,
tags: vec![1, 2],
tag_input_string: None,
season_folder: false,
add_options: AddSeriesOptions {
monitor: "future".to_owned(),
@@ -522,7 +527,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
SonarrEvent::AddSeries(Some(expected_add_series_body)).into(),
SonarrEvent::AddSeries(expected_add_series_body).into(),
))
.times(1)
.returning(|_| {
+6 -6
View File
@@ -94,35 +94,35 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrDeleteCommand> for SonarrDeleteComm
SonarrDeleteCommand::BlocklistItem { blocklist_item_id } => {
let resp = self
.network
.handle_network_event(SonarrEvent::DeleteBlocklistItem(Some(blocklist_item_id)).into())
.handle_network_event(SonarrEvent::DeleteBlocklistItem(blocklist_item_id).into())
.await?;
serde_json::to_string_pretty(&resp)?
}
SonarrDeleteCommand::Download { download_id } => {
let resp = self
.network
.handle_network_event(SonarrEvent::DeleteDownload(Some(download_id)).into())
.handle_network_event(SonarrEvent::DeleteDownload(download_id).into())
.await?;
serde_json::to_string_pretty(&resp)?
}
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)?
}
SonarrDeleteCommand::Indexer { indexer_id } => {
let resp = self
.network
.handle_network_event(SonarrEvent::DeleteIndexer(Some(indexer_id)).into())
.handle_network_event(SonarrEvent::DeleteIndexer(indexer_id).into())
.await?;
serde_json::to_string_pretty(&resp)?
}
SonarrDeleteCommand::RootFolder { root_folder_id } => {
let resp = self
.network
.handle_network_event(SonarrEvent::DeleteRootFolder(Some(root_folder_id)).into())
.handle_network_event(SonarrEvent::DeleteRootFolder(root_folder_id).into())
.await?;
serde_json::to_string_pretty(&resp)?
}
@@ -138,7 +138,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrDeleteCommand> for SonarrDeleteComm
};
let resp = self
.network
.handle_network_event(SonarrEvent::DeleteSeries(Some(delete_series_params)).into())
.handle_network_event(SonarrEvent::DeleteSeries(delete_series_params).into())
.await?;
serde_json::to_string_pretty(&resp)?
}
+31 -5
View File
@@ -301,7 +301,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
SonarrEvent::DeleteBlocklistItem(Some(expected_blocklist_item_id)).into(),
SonarrEvent::DeleteBlocklistItem(expected_blocklist_item_id).into(),
))
.times(1)
.returning(|_| {
@@ -332,7 +332,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
SonarrEvent::DeleteDownload(Some(expected_download_id)).into(),
SonarrEvent::DeleteDownload(expected_download_id).into(),
))
.times(1)
.returning(|_| {
@@ -351,6 +351,32 @@ 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::<NetworkEvent>(
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;
@@ -358,7 +384,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
SonarrEvent::DeleteIndexer(Some(expected_indexer_id)).into(),
SonarrEvent::DeleteIndexer(expected_indexer_id).into(),
))
.times(1)
.returning(|_| {
@@ -384,7 +410,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
SonarrEvent::DeleteRootFolder(Some(expected_root_folder_id)).into(),
SonarrEvent::DeleteRootFolder(expected_root_folder_id).into(),
))
.times(1)
.returning(|_| {
@@ -414,7 +440,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
SonarrEvent::DeleteSeries(Some(expected_delete_series_params)).into(),
SonarrEvent::DeleteSeries(expected_delete_series_params).into(),
))
.times(1)
.returning(|_| {
+5 -3
View File
@@ -274,7 +274,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrEditCommand> for SonarrEditCommandH
};
self
.network
.handle_network_event(SonarrEvent::EditAllIndexerSettings(Some(params)).into())
.handle_network_event(SonarrEvent::EditAllIndexerSettings(params).into())
.await?;
"All indexer settings updated".to_owned()
} else {
@@ -312,13 +312,14 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrEditCommand> for SonarrEditCommandH
api_key,
seed_ratio,
tags: tag,
tag_input_string: None,
priority,
clear_tags,
};
self
.network
.handle_network_event(SonarrEvent::EditIndexer(Some(edit_indexer_params)).into())
.handle_network_event(SonarrEvent::EditIndexer(edit_indexer_params).into())
.await?;
"Indexer updated".to_owned()
}
@@ -347,12 +348,13 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrEditCommand> for SonarrEditCommandH
language_profile_id,
root_folder_path,
tags: tag,
tag_input_string: None,
clear_tags,
};
self
.network
.handle_network_event(SonarrEvent::EditSeries(Some(edit_series_params)).into())
.handle_network_event(SonarrEvent::EditSeries(edit_series_params).into())
.await?;
"Series Updated".to_owned()
}
+9 -5
View File
@@ -650,7 +650,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
SonarrEvent::EditAllIndexerSettings(Some(expected_edit_all_indexer_settings)).into(),
SonarrEvent::EditAllIndexerSettings(expected_edit_all_indexer_settings).into(),
))
.times(1)
.returning(|_| {
@@ -689,6 +689,7 @@ mod tests {
api_key: Some("testKey".to_owned()),
seed_ratio: Some("1.2".to_owned()),
tags: Some(vec![1, 2]),
tag_input_string: None,
priority: Some(25),
clear_tags: false,
};
@@ -696,7 +697,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
SonarrEvent::EditIndexer(Some(expected_edit_indexer_params)).into(),
SonarrEvent::EditIndexer(expected_edit_indexer_params).into(),
))
.times(1)
.returning(|_| {
@@ -741,13 +742,14 @@ mod tests {
language_profile_id: Some(1),
root_folder_path: Some("/nfs/test".to_owned()),
tags: Some(vec![1, 2]),
tag_input_string: None,
clear_tags: false,
};
let mut mock_network = MockNetworkTrait::new();
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
SonarrEvent::EditSeries(Some(expected_edit_series_params)).into(),
SonarrEvent::EditSeries(expected_edit_series_params).into(),
))
.times(1)
.returning(|_| {
@@ -788,13 +790,14 @@ mod tests {
language_profile_id: Some(1),
root_folder_path: Some("/nfs/test".to_owned()),
tags: Some(vec![1, 2]),
tag_input_string: None,
clear_tags: false,
};
let mut mock_network = MockNetworkTrait::new();
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
SonarrEvent::EditSeries(Some(expected_edit_series_params)).into(),
SonarrEvent::EditSeries(expected_edit_series_params).into(),
))
.times(1)
.returning(|_| {
@@ -835,13 +838,14 @@ mod tests {
language_profile_id: Some(1),
root_folder_path: Some("/nfs/test".to_owned()),
tags: Some(vec![1, 2]),
tag_input_string: None,
clear_tags: false,
};
let mut mock_network = MockNetworkTrait::new();
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
SonarrEvent::EditSeries(Some(expected_edit_series_params)).into(),
SonarrEvent::EditSeries(expected_edit_series_params).into(),
))
.times(1)
.returning(|_| {
+2 -2
View File
@@ -83,7 +83,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrGetCommand> for SonarrGetCommandHan
SonarrGetCommand::EpisodeDetails { episode_id } => {
let resp = self
.network
.handle_network_event(SonarrEvent::GetEpisodeDetails(Some(episode_id)).into())
.handle_network_event(SonarrEvent::GetEpisodeDetails(episode_id).into())
.await?;
serde_json::to_string_pretty(&resp)?
}
@@ -104,7 +104,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrGetCommand> for SonarrGetCommandHan
SonarrGetCommand::SeriesDetails { series_id } => {
let resp = self
.network
.handle_network_event(SonarrEvent::GetSeriesDetails(Some(series_id)).into())
.handle_network_event(SonarrEvent::GetSeriesDetails(series_id).into())
.await?;
serde_json::to_string_pretty(&resp)?
}
+2 -2
View File
@@ -160,7 +160,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
SonarrEvent::GetEpisodeDetails(Some(expected_episode_id)).into(),
SonarrEvent::GetEpisodeDetails(expected_episode_id).into(),
))
.times(1)
.returning(|_| {
@@ -232,7 +232,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
SonarrEvent::GetSeriesDetails(Some(expected_series_id)).into(),
SonarrEvent::GetSeriesDetails(expected_series_id).into(),
))
.times(1)
.returning(|_| {
+7 -9
View File
@@ -163,28 +163,28 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrListCommand> for SonarrListCommandH
SonarrListCommand::Episodes { series_id } => {
let resp = self
.network
.handle_network_event(SonarrEvent::GetEpisodes(Some(series_id)).into())
.handle_network_event(SonarrEvent::GetEpisodes(series_id).into())
.await?;
serde_json::to_string_pretty(&resp)?
}
SonarrListCommand::EpisodeFiles { series_id } => {
let resp = self
.network
.handle_network_event(SonarrEvent::GetEpisodeFiles(Some(series_id)).into())
.handle_network_event(SonarrEvent::GetEpisodeFiles(series_id).into())
.await?;
serde_json::to_string_pretty(&resp)?
}
SonarrListCommand::EpisodeHistory { episode_id } => {
let resp = self
.network
.handle_network_event(SonarrEvent::GetEpisodeHistory(Some(episode_id)).into())
.handle_network_event(SonarrEvent::GetEpisodeHistory(episode_id).into())
.await?;
serde_json::to_string_pretty(&resp)?
}
SonarrListCommand::History { events: items } => {
let resp = self
.network
.handle_network_event(SonarrEvent::GetHistory(Some(items)).into())
.handle_network_event(SonarrEvent::GetHistory(items).into())
.await?;
serde_json::to_string_pretty(&resp)?
}
@@ -208,7 +208,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrListCommand> for SonarrListCommandH
} => {
let logs = self
.network
.handle_network_event(SonarrEvent::GetLogs(Some(events)).into())
.handle_network_event(SonarrEvent::GetLogs(events).into())
.await?;
if output_in_log_format {
@@ -246,9 +246,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrListCommand> for SonarrListCommandH
} => {
let resp = self
.network
.handle_network_event(
SonarrEvent::GetSeasonHistory(Some((series_id, season_number))).into(),
)
.handle_network_event(SonarrEvent::GetSeasonHistory((series_id, season_number)).into())
.await?;
serde_json::to_string_pretty(&resp)?
}
@@ -262,7 +260,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrListCommand> for SonarrListCommandH
SonarrListCommand::SeriesHistory { series_id } => {
let resp = self
.network
.handle_network_event(SonarrEvent::GetSeriesHistory(Some(series_id)).into())
.handle_network_event(SonarrEvent::GetSeriesHistory(series_id).into())
.await?;
serde_json::to_string_pretty(&resp)?
}
+7 -7
View File
@@ -329,7 +329,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
SonarrEvent::GetEpisodes(Some(expected_series_id)).into(),
SonarrEvent::GetEpisodes(expected_series_id).into(),
))
.times(1)
.returning(|_| {
@@ -355,7 +355,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
SonarrEvent::GetEpisodeFiles(Some(expected_series_id)).into(),
SonarrEvent::GetEpisodeFiles(expected_series_id).into(),
))
.times(1)
.returning(|_| {
@@ -381,7 +381,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
SonarrEvent::GetHistory(Some(expected_events)).into(),
SonarrEvent::GetHistory(expected_events).into(),
))
.times(1)
.returning(|_| {
@@ -407,7 +407,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
SonarrEvent::GetLogs(Some(expected_events)).into(),
SonarrEvent::GetLogs(expected_events).into(),
))
.times(1)
.returning(|_| {
@@ -435,7 +435,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
SonarrEvent::GetSeriesHistory(Some(expected_series_id)).into(),
SonarrEvent::GetSeriesHistory(expected_series_id).into(),
))
.times(1)
.returning(|_| {
@@ -461,7 +461,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
SonarrEvent::GetEpisodeHistory(Some(expected_episode_id)).into(),
SonarrEvent::GetEpisodeHistory(expected_episode_id).into(),
))
.times(1)
.returning(|_| {
@@ -488,7 +488,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
SonarrEvent::GetSeasonHistory(Some((expected_series_id, expected_season_number))).into(),
SonarrEvent::GetSeasonHistory((expected_series_id, expected_season_number)).into(),
))
.times(1)
.returning(|_| {
@@ -75,7 +75,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrManualSearchCommand>
println!("Searching for episode releases. This may take a minute...");
let resp = self
.network
.handle_network_event(SonarrEvent::GetEpisodeReleases(Some(episode_id)).into())
.handle_network_event(SonarrEvent::GetEpisodeReleases(episode_id).into())
.await?;
serde_json::to_string_pretty(&resp)?
}
@@ -86,9 +86,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrManualSearchCommand>
println!("Searching for season releases. This may take a minute...");
let resp = self
.network
.handle_network_event(
SonarrEvent::GetSeasonReleases(Some((series_id, season_number))).into(),
)
.handle_network_event(SonarrEvent::GetSeasonReleases((series_id, season_number)).into())
.await?;
serde_json::to_string_pretty(&resp)?
}
@@ -130,7 +130,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
SonarrEvent::GetEpisodeReleases(Some(expected_episode_id)).into(),
SonarrEvent::GetEpisodeReleases(expected_episode_id).into(),
))
.times(1)
.returning(|_| {
@@ -160,7 +160,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
SonarrEvent::GetSeasonReleases(Some((expected_series_id, expected_season_number))).into(),
SonarrEvent::GetSeasonReleases((expected_series_id, expected_season_number)).into(),
))
.times(1)
.returning(|_| {
+5 -5
View File
@@ -245,21 +245,21 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrCommand> for SonarrCliHandler<'a, '
SonarrCommand::SearchNewSeries { query } => {
let resp = self
.network
.handle_network_event(SonarrEvent::SearchNewSeries(Some(query)).into())
.handle_network_event(SonarrEvent::SearchNewSeries(query).into())
.await?;
serde_json::to_string_pretty(&resp)?
}
SonarrCommand::StartTask { task_name } => {
let resp = self
.network
.handle_network_event(SonarrEvent::StartTask(Some(task_name)).into())
.handle_network_event(SonarrEvent::StartTask(task_name).into())
.await?;
serde_json::to_string_pretty(&resp)?
}
SonarrCommand::TestIndexer { indexer_id } => {
let resp = self
.network
.handle_network_event(SonarrEvent::TestIndexer(Some(indexer_id)).into())
.handle_network_event(SonarrEvent::TestIndexer(indexer_id).into())
.await?;
serde_json::to_string_pretty(&resp)?
}
@@ -274,7 +274,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrCommand> for SonarrCliHandler<'a, '
SonarrCommand::ToggleEpisodeMonitoring { episode_id } => {
let resp = self
.network
.handle_network_event(SonarrEvent::ToggleEpisodeMonitoring(Some(episode_id)).into())
.handle_network_event(SonarrEvent::ToggleEpisodeMonitoring(episode_id).into())
.await?;
serde_json::to_string_pretty(&resp)?
}
@@ -285,7 +285,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrCommand> for SonarrCliHandler<'a, '
let resp = self
.network
.handle_network_event(
SonarrEvent::ToggleSeasonMonitoring(Some((series_id, season_number))).into(),
SonarrEvent::ToggleSeasonMonitoring((series_id, season_number)).into(),
)
.await?;
serde_json::to_string_pretty(&resp)?
+1 -1
View File
@@ -71,7 +71,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrRefreshCommand>
SonarrRefreshCommand::Series { series_id } => {
let resp = self
.network
.handle_network_event(SonarrEvent::UpdateAndScanSeries(Some(series_id)).into())
.handle_network_event(SonarrEvent::UpdateAndScanSeries(series_id).into())
.await?;
serde_json::to_string_pretty(&resp)?
}
@@ -119,7 +119,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
SonarrEvent::UpdateAndScanSeries(Some(expected_series_id)).into(),
SonarrEvent::UpdateAndScanSeries(expected_series_id).into(),
))
.times(1)
.returning(|_| {
+10 -11
View File
@@ -346,7 +346,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
SonarrEvent::DeleteBlocklistItem(Some(expected_blocklist_item_id)).into(),
SonarrEvent::DeleteBlocklistItem(expected_blocklist_item_id).into(),
))
.times(1)
.returning(|_| {
@@ -434,7 +434,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
SonarrEvent::EditAllIndexerSettings(Some(expected_edit_all_indexer_settings)).into(),
SonarrEvent::EditAllIndexerSettings(expected_edit_all_indexer_settings).into(),
))
.times(1)
.returning(|_| {
@@ -470,7 +470,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
SonarrEvent::GetEpisodeReleases(Some(expected_episode_id)).into(),
SonarrEvent::GetEpisodeReleases(expected_episode_id).into(),
))
.times(1)
.returning(|_| {
@@ -498,7 +498,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
SonarrEvent::TriggerAutomaticEpisodeSearch(Some(expected_episode_id)).into(),
SonarrEvent::TriggerAutomaticEpisodeSearch(expected_episode_id).into(),
))
.times(1)
.returning(|_| {
@@ -571,7 +571,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
SonarrEvent::UpdateAndScanSeries(Some(expected_series_id)).into(),
SonarrEvent::UpdateAndScanSeries(expected_series_id).into(),
))
.times(1)
.returning(|_| {
@@ -597,7 +597,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
SonarrEvent::SearchNewSeries(Some(expected_search_query)).into(),
SonarrEvent::SearchNewSeries(expected_search_query).into(),
))
.times(1)
.returning(|_| {
@@ -624,7 +624,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
SonarrEvent::StartTask(Some(expected_task_name)).into(),
SonarrEvent::StartTask(expected_task_name).into(),
))
.times(1)
.returning(|_| {
@@ -651,7 +651,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
SonarrEvent::TestIndexer(Some(expected_indexer_id)).into(),
SonarrEvent::TestIndexer(expected_indexer_id).into(),
))
.times(1)
.returning(|_| {
@@ -698,7 +698,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
SonarrEvent::ToggleEpisodeMonitoring(Some(expected_episode_id)).into(),
SonarrEvent::ToggleEpisodeMonitoring(expected_episode_id).into(),
))
.times(1)
.returning(|_| {
@@ -729,8 +729,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
SonarrEvent::ToggleSeasonMonitoring(Some((expected_series_id, expected_season_number)))
.into(),
SonarrEvent::ToggleSeasonMonitoring((expected_series_id, expected_season_number)).into(),
))
.times(1)
.returning(|_| {
@@ -83,7 +83,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrTriggerAutomaticSearchCommand>
SonarrTriggerAutomaticSearchCommand::Series { series_id } => {
let resp = self
.network
.handle_network_event(SonarrEvent::TriggerAutomaticSeriesSearch(Some(series_id)).into())
.handle_network_event(SonarrEvent::TriggerAutomaticSeriesSearch(series_id).into())
.await?;
serde_json::to_string_pretty(&resp)?
}
@@ -94,7 +94,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrTriggerAutomaticSearchCommand>
let resp = self
.network
.handle_network_event(
SonarrEvent::TriggerAutomaticSeasonSearch(Some((series_id, season_number))).into(),
SonarrEvent::TriggerAutomaticSeasonSearch((series_id, season_number)).into(),
)
.await?;
serde_json::to_string_pretty(&resp)?
@@ -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)?
}
@@ -166,7 +166,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
SonarrEvent::TriggerAutomaticSeriesSearch(Some(expected_series_id)).into(),
SonarrEvent::TriggerAutomaticSeriesSearch(expected_series_id).into(),
))
.times(1)
.returning(|_| {
@@ -197,11 +197,8 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
SonarrEvent::TriggerAutomaticSeasonSearch(Some((
expected_series_id,
expected_season_number,
)))
.into(),
SonarrEvent::TriggerAutomaticSeasonSearch((expected_series_id, expected_season_number))
.into(),
))
.times(1)
.returning(|_| {
@@ -233,7 +230,7 @@ mod tests {
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
SonarrEvent::TriggerAutomaticEpisodeSearch(Some(expected_episode_id)).into(),
SonarrEvent::TriggerAutomaticEpisodeSearch(expected_episode_id).into(),
))
.times(1)
.returning(|_| {
@@ -160,7 +160,7 @@ mod tests {
#[case(
ActiveRadarrBlock::Blocklist,
ActiveRadarrBlock::DeleteBlocklistItemPrompt,
RadarrEvent::DeleteBlocklistItem(None)
RadarrEvent::DeleteBlocklistItem(3)
)]
#[case(
ActiveRadarrBlock::Blocklist,
@@ -361,7 +361,7 @@ mod tests {
#[case(
ActiveRadarrBlock::Blocklist,
ActiveRadarrBlock::DeleteBlocklistItemPrompt,
RadarrEvent::DeleteBlocklistItem(None)
RadarrEvent::DeleteBlocklistItem(3)
)]
#[case(
ActiveRadarrBlock::Blocklist,
@@ -541,6 +541,22 @@ mod tests {
})
}
#[test]
fn test_extract_blocklist_item_id() {
let mut app = App::default();
app.data.radarr_data.blocklist.set_items(blocklist_vec());
let blocklist_item_id = BlocklistHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveRadarrBlock::Blocklist,
None,
)
.extract_blocklist_item_id();
assert_eq!(blocklist_item_id, 3);
}
#[test]
fn test_blocklist_handler_not_ready_when_loading() {
let mut app = App::default();
+10 -4
View File
@@ -28,6 +28,10 @@ impl<'a, 'b> BlocklistHandler<'a, 'b> {
self.app.data.radarr_data.blocklist,
BlocklistItem
);
fn extract_blocklist_item_id(&self) -> i64 {
self.app.data.radarr_data.blocklist.current_selection().id
}
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for BlocklistHandler<'a, 'b> {
@@ -98,8 +102,9 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for BlocklistHandler<'a,
match self.active_radarr_block {
ActiveRadarrBlock::DeleteBlocklistItemPrompt => {
if self.app.data.radarr_data.prompt_confirm {
self.app.data.radarr_data.prompt_confirm_action =
Some(RadarrEvent::DeleteBlocklistItem(None));
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::DeleteBlocklistItem(
self.extract_blocklist_item_id(),
));
}
self.app.pop_navigation_stack();
@@ -151,8 +156,9 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for BlocklistHandler<'a,
ActiveRadarrBlock::DeleteBlocklistItemPrompt => {
if key == DEFAULT_KEYBINDINGS.confirm.key {
self.app.data.radarr_data.prompt_confirm = true;
self.app.data.radarr_data.prompt_confirm_action =
Some(RadarrEvent::DeleteBlocklistItem(None));
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::DeleteBlocklistItem(
self.extract_blocklist_item_id(),
));
self.app.pop_navigation_stack();
}
@@ -2,6 +2,8 @@ use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handlers::{handle_prompt_toggle, KeyEventHandler};
use crate::models::radarr_models::EditCollectionParams;
use crate::models::servarr_data::radarr::modals::EditCollectionModal;
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, EDIT_COLLECTION_BLOCKS};
use crate::models::Scrollable;
use crate::network::radarr_network::RadarrEvent;
@@ -18,6 +20,51 @@ pub(super) struct EditCollectionHandler<'a, 'b> {
context: Option<ActiveRadarrBlock>,
}
impl<'a, 'b> EditCollectionHandler<'a, 'b> {
fn build_edit_collection_params(&mut self) -> EditCollectionParams {
let collection_id = self.app.data.radarr_data.collections.current_selection().id;
let EditCollectionModal {
path,
search_on_add,
minimum_availability_list,
monitored,
quality_profile_list,
} = self
.app
.data
.radarr_data
.edit_collection_modal
.as_ref()
.unwrap();
let quality_profile = quality_profile_list.current_selection();
let quality_profile_id = *self
.app
.data
.radarr_data
.quality_profile_map
.iter()
.filter(|(_, value)| *value == quality_profile)
.map(|(key, _)| key)
.next()
.unwrap();
let root_folder_path: String = path.text.clone();
let monitored = monitored.unwrap_or_default();
let search_on_add = search_on_add.unwrap_or_default();
let minimum_availability = *minimum_availability_list.current_selection();
self.app.data.radarr_data.edit_collection_modal = None;
EditCollectionParams {
collection_id,
monitored: Some(monitored),
minimum_availability: Some(minimum_availability),
quality_profile_id: Some(quality_profile_id),
root_folder_path: Some(root_folder_path),
search_on_add: Some(search_on_add),
}
}
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditCollectionHandler<'a, 'b> {
fn accepts(active_block: ActiveRadarrBlock) -> bool {
EDIT_COLLECTION_BLOCKS.contains(&active_block)
@@ -190,8 +237,9 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditCollectionHandle
match self.app.data.radarr_data.selected_block.get_active_block() {
ActiveRadarrBlock::EditCollectionConfirmPrompt => {
if self.app.data.radarr_data.prompt_confirm {
self.app.data.radarr_data.prompt_confirm_action =
Some(RadarrEvent::EditCollection(None));
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::EditCollection(
self.build_edit_collection_params(),
));
self.app.should_refresh = true;
}
@@ -310,7 +358,9 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditCollectionHandle
&& key == DEFAULT_KEYBINDINGS.confirm.key
{
self.app.data.radarr_data.prompt_confirm = true;
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::EditCollection(None));
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::EditCollection(
self.build_edit_collection_params(),
));
self.app.should_refresh = true;
self.app.pop_navigation_stack();
@@ -1,5 +1,6 @@
#[cfg(test)]
mod tests {
use bimap::BiMap;
use pretty_assertions::assert_str_eq;
use strum::IntoEnumIterator;
@@ -7,8 +8,9 @@ mod tests {
use crate::app::App;
use crate::event::Key;
use crate::handlers::radarr_handlers::collections::edit_collection_handler::EditCollectionHandler;
use crate::handlers::radarr_handlers::radarr_handler_test_utils::utils::collection;
use crate::handlers::KeyEventHandler;
use crate::models::radarr_models::MinimumAvailability;
use crate::models::radarr_models::{Collection, EditCollectionParams, MinimumAvailability};
use crate::models::servarr_data::radarr::modals::EditCollectionModal;
use crate::models::servarr_data::radarr::radarr_data::{
ActiveRadarrBlock, EDIT_COLLECTION_BLOCKS,
@@ -519,7 +521,34 @@ mod tests {
#[test]
fn test_edit_collection_confirm_prompt_prompt_confirmation_submit() {
let mut app = App::default();
app.data.radarr_data.edit_collection_modal = Some(EditCollectionModal::default());
let mut edit_collection_modal = EditCollectionModal {
path: "/nfs/Test Path".into(),
monitored: Some(false),
search_on_add: Some(false),
..EditCollectionModal::default()
};
edit_collection_modal
.quality_profile_list
.set_items(vec!["Any".to_owned(), "HD - 1080p".to_owned()]);
edit_collection_modal
.minimum_availability_list
.set_items(Vec::from_iter(MinimumAvailability::iter()));
app.data.radarr_data.edit_collection_modal = Some(edit_collection_modal);
app.data.radarr_data.collections.set_items(vec![Collection {
monitored: false,
search_on_add: false,
..collection()
}]);
app.data.radarr_data.quality_profile_map =
BiMap::from_iter([(1111, "Any".to_owned()), (2222, "HD - 1080p".to_owned())]);
let expected_edit_collection_params = EditCollectionParams {
collection_id: 123,
monitored: Some(false),
minimum_availability: Some(MinimumAvailability::Announced),
quality_profile_id: Some(1111),
root_folder_path: Some("/nfs/Test Path".to_owned()),
search_on_add: Some(false),
};
app.push_navigation_stack(ActiveRadarrBlock::Collections.into());
app.push_navigation_stack(ActiveRadarrBlock::EditCollectionPrompt.into());
app.data.radarr_data.prompt_confirm = true;
@@ -545,9 +574,10 @@ mod tests {
);
assert_eq!(
app.data.radarr_data.prompt_confirm_action,
Some(RadarrEvent::EditCollection(None))
Some(RadarrEvent::EditCollection(expected_edit_collection_params))
);
assert!(app.should_refresh);
assert!(app.data.radarr_data.edit_collection_modal.is_none());
}
#[test]
@@ -919,7 +949,34 @@ mod tests {
#[test]
fn test_edit_collection_confirm_prompt_prompt_confirmation_confirm() {
let mut app = App::default();
app.data.radarr_data.edit_collection_modal = Some(EditCollectionModal::default());
let mut edit_collection_modal = EditCollectionModal {
path: "/nfs/Test Path".into(),
monitored: Some(false),
search_on_add: Some(false),
..EditCollectionModal::default()
};
edit_collection_modal
.quality_profile_list
.set_items(vec!["Any".to_owned(), "HD - 1080p".to_owned()]);
edit_collection_modal
.minimum_availability_list
.set_items(Vec::from_iter(MinimumAvailability::iter()));
app.data.radarr_data.edit_collection_modal = Some(edit_collection_modal);
app.data.radarr_data.collections.set_items(vec![Collection {
monitored: false,
search_on_add: false,
..collection()
}]);
app.data.radarr_data.quality_profile_map =
BiMap::from_iter([(1111, "Any".to_owned()), (2222, "HD - 1080p".to_owned())]);
let expected_edit_collection_params = EditCollectionParams {
collection_id: 123,
monitored: Some(false),
minimum_availability: Some(MinimumAvailability::Announced),
quality_profile_id: Some(1111),
root_folder_path: Some("/nfs/Test Path".to_owned()),
search_on_add: Some(false),
};
app.push_navigation_stack(ActiveRadarrBlock::Collections.into());
app.push_navigation_stack(ActiveRadarrBlock::EditCollectionPrompt.into());
app.data.radarr_data.selected_block =
@@ -944,9 +1001,10 @@ mod tests {
);
assert_eq!(
app.data.radarr_data.prompt_confirm_action,
Some(RadarrEvent::EditCollection(None))
Some(RadarrEvent::EditCollection(expected_edit_collection_params))
);
assert!(app.should_refresh);
assert!(app.data.radarr_data.edit_collection_modal.is_none());
}
}
@@ -961,6 +1019,50 @@ mod tests {
});
}
#[test]
fn test_build_edit_collection_params() {
let mut app = App::default();
let mut edit_collection_modal = EditCollectionModal {
path: "/nfs/Test Path".into(),
monitored: Some(false),
search_on_add: Some(false),
..EditCollectionModal::default()
};
edit_collection_modal
.quality_profile_list
.set_items(vec!["Any".to_owned(), "HD - 1080p".to_owned()]);
edit_collection_modal
.minimum_availability_list
.set_items(Vec::from_iter(MinimumAvailability::iter()));
app.data.radarr_data.edit_collection_modal = Some(edit_collection_modal);
app.data.radarr_data.collections.set_items(vec![Collection {
monitored: false,
search_on_add: false,
..collection()
}]);
app.data.radarr_data.quality_profile_map =
BiMap::from_iter([(1111, "Any".to_owned()), (2222, "HD - 1080p".to_owned())]);
let expected_edit_collection_params = EditCollectionParams {
collection_id: 123,
monitored: Some(false),
minimum_availability: Some(MinimumAvailability::Announced),
quality_profile_id: Some(1111),
root_folder_path: Some("/nfs/Test Path".to_owned()),
search_on_add: Some(false),
};
let edit_collection_params = EditCollectionHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveRadarrBlock::EditCollectionPrompt,
None,
)
.build_edit_collection_params();
assert_eq!(edit_collection_params, expected_edit_collection_params);
assert!(app.data.radarr_data.edit_collection_modal.is_none());
}
#[test]
fn test_edit_collection_handler_is_not_ready_when_loading() {
let mut app = App::default();
@@ -1,11 +1,13 @@
#[cfg(test)]
mod tests {
use pretty_assertions::assert_eq;
use strum::IntoEnumIterator;
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handlers::radarr_handlers::downloads::DownloadsHandler;
use crate::handlers::radarr_handlers::radarr_handler_test_utils::utils::download_record;
use crate::handlers::KeyEventHandler;
use crate::models::radarr_models::DownloadRecord;
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, DOWNLOADS_BLOCKS};
@@ -137,7 +139,7 @@ mod tests {
#[case(
ActiveRadarrBlock::Downloads,
ActiveRadarrBlock::DeleteDownloadPrompt,
RadarrEvent::DeleteDownload(None)
RadarrEvent::DeleteDownload(1)
)]
#[case(
ActiveRadarrBlock::Downloads,
@@ -154,7 +156,7 @@ mod tests {
.data
.radarr_data
.downloads
.set_items(vec![DownloadRecord::default()]);
.set_items(vec![download_record()]);
app.data.radarr_data.prompt_confirm = true;
app.push_navigation_stack(base_route.into());
app.push_navigation_stack(prompt_block.into());
@@ -336,7 +338,7 @@ mod tests {
#[case(
ActiveRadarrBlock::Downloads,
ActiveRadarrBlock::DeleteDownloadPrompt,
RadarrEvent::DeleteDownload(None)
RadarrEvent::DeleteDownload(1)
)]
#[case(
ActiveRadarrBlock::Downloads,
@@ -353,7 +355,7 @@ mod tests {
.data
.radarr_data
.downloads
.set_items(vec![DownloadRecord::default()]);
.set_items(vec![download_record()]);
app.push_navigation_stack(base_route.into());
app.push_navigation_stack(prompt_block.into());
@@ -385,6 +387,26 @@ mod tests {
})
}
#[test]
fn test_extract_download_id() {
let mut app = App::default();
app
.data
.radarr_data
.downloads
.set_items(vec![download_record()]);
let download_id = DownloadsHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveRadarrBlock::Downloads,
None,
)
.extract_download_id();
assert_eq!(download_id, 1);
}
#[test]
fn test_downloads_handler_not_ready_when_loading() {
let mut app = App::default();
@@ -27,6 +27,10 @@ impl<'a, 'b> DownloadsHandler<'a, 'b> {
self.app.data.radarr_data.downloads,
DownloadRecord
);
fn extract_download_id(&self) -> i64 {
self.app.data.radarr_data.downloads.current_selection().id
}
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for DownloadsHandler<'a, 'b> {
@@ -95,7 +99,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for DownloadsHandler<'a,
match self.active_radarr_block {
ActiveRadarrBlock::DeleteDownloadPrompt => {
if self.app.data.radarr_data.prompt_confirm {
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::DeleteDownload(None));
self.app.data.radarr_data.prompt_confirm_action =
Some(RadarrEvent::DeleteDownload(self.extract_download_id()));
}
self.app.pop_navigation_stack();
@@ -138,7 +143,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for DownloadsHandler<'a,
ActiveRadarrBlock::DeleteDownloadPrompt => {
if key == DEFAULT_KEYBINDINGS.confirm.key {
self.app.data.radarr_data.prompt_confirm = true;
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::DeleteDownload(None));
self.app.data.radarr_data.prompt_confirm_action =
Some(RadarrEvent::DeleteDownload(self.extract_download_id()));
self.app.pop_navigation_stack();
}
@@ -2,7 +2,9 @@ use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handlers::{handle_prompt_toggle, KeyEventHandler};
use crate::models::servarr_data::modals::EditIndexerModal;
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, EDIT_INDEXER_BLOCKS};
use crate::models::servarr_models::EditIndexerParams;
use crate::network::radarr_network::RadarrEvent;
use crate::{handle_prompt_left_right_keys, handle_text_box_keys, handle_text_box_left_right_keys};
@@ -17,6 +19,61 @@ pub(super) struct EditIndexerHandler<'a, 'b> {
_context: Option<ActiveRadarrBlock>,
}
impl<'a, 'b> EditIndexerHandler<'a, 'b> {
fn build_edit_indexer_params(&mut self) -> EditIndexerParams {
let indexer_id = self.app.data.radarr_data.indexers.current_selection().id;
let tags = self
.app
.data
.radarr_data
.edit_indexer_modal
.as_ref()
.unwrap()
.tags
.text
.clone();
let params = {
let EditIndexerModal {
name,
enable_rss,
enable_automatic_search,
enable_interactive_search,
url,
api_key,
seed_ratio,
priority,
..
} = self
.app
.data
.radarr_data
.edit_indexer_modal
.as_ref()
.unwrap();
EditIndexerParams {
indexer_id,
name: Some(name.text.clone()),
enable_rss: Some(enable_rss.unwrap_or_default()),
enable_automatic_search: Some(enable_automatic_search.unwrap_or_default()),
enable_interactive_search: Some(enable_interactive_search.unwrap_or_default()),
url: Some(url.text.clone()),
api_key: Some(api_key.text.clone()),
seed_ratio: Some(seed_ratio.text.clone()),
tags: None,
tag_input_string: Some(tags),
priority: Some(*priority),
clear_tags: false,
}
};
self.app.data.radarr_data.edit_indexer_modal = None;
params
}
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditIndexerHandler<'a, 'b> {
fn accepts(active_block: ActiveRadarrBlock) -> bool {
EDIT_INDEXER_BLOCKS.contains(&active_block)
@@ -297,12 +354,12 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditIndexerHandler<'
let selected_block = self.app.data.radarr_data.selected_block.get_active_block();
match selected_block {
ActiveRadarrBlock::EditIndexerConfirmPrompt => {
let radarr_data = &mut self.app.data.radarr_data;
if radarr_data.prompt_confirm {
radarr_data.prompt_confirm_action = Some(RadarrEvent::EditIndexer(None));
if self.app.data.radarr_data.prompt_confirm {
self.app.data.radarr_data.prompt_confirm_action =
Some(RadarrEvent::EditIndexer(self.build_edit_indexer_params()));
self.app.should_refresh = true;
} else {
radarr_data.edit_indexer_modal = None;
self.app.data.radarr_data.edit_indexer_modal = None;
}
self.app.pop_navigation_stack();
@@ -464,7 +521,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditIndexerHandler<'
&& self.key == DEFAULT_KEYBINDINGS.confirm.key
{
self.app.data.radarr_data.prompt_confirm = true;
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::EditIndexer(None));
self.app.data.radarr_data.prompt_confirm_action =
Some(RadarrEvent::EditIndexer(self.build_edit_indexer_params()));
self.app.should_refresh = true;
self.app.pop_navigation_stack();
@@ -4,9 +4,12 @@ mod tests {
use crate::app::App;
use crate::event::Key;
use crate::handlers::radarr_handlers::indexers::edit_indexer_handler::EditIndexerHandler;
use crate::handlers::radarr_handlers::radarr_handler_test_utils::utils::indexer;
use crate::handlers::KeyEventHandler;
use crate::models::servarr_data::modals::EditIndexerModal;
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, EDIT_INDEXER_BLOCKS};
use crate::models::servarr_models::EditIndexerParams;
use pretty_assertions::assert_eq;
use strum::IntoEnumIterator;
mod test_handle_scroll_up_and_down {
@@ -896,7 +899,32 @@ mod tests {
.radarr_data
.selected_block
.set_index(0, EDIT_INDEXER_TORRENT_SELECTION_BLOCKS.len() - 1);
app.data.radarr_data.edit_indexer_modal = Some(EditIndexerModal::default());
let edit_indexer_modal = EditIndexerModal {
name: "Test Update".into(),
enable_rss: Some(false),
enable_automatic_search: Some(false),
enable_interactive_search: Some(false),
url: "https://localhost:9696/1/".into(),
api_key: "test1234".into(),
seed_ratio: "1.3".into(),
tags: "usenet, testing".into(),
priority: 25,
};
app.data.radarr_data.edit_indexer_modal = Some(edit_indexer_modal);
app.data.radarr_data.indexers.set_items(vec![indexer()]);
let expected_edit_indexer_params = EditIndexerParams {
indexer_id: 1,
name: Some("Test Update".to_owned()),
enable_rss: Some(false),
enable_automatic_search: Some(false),
enable_interactive_search: Some(false),
url: Some("https://localhost:9696/1/".to_owned()),
api_key: Some("test1234".to_owned()),
seed_ratio: Some("1.3".to_owned()),
tag_input_string: Some("usenet, testing".to_owned()),
priority: Some(25),
..EditIndexerParams::default()
};
app.data.radarr_data.prompt_confirm = true;
EditIndexerHandler::with(
@@ -908,11 +936,11 @@ mod tests {
.handle();
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
assert!(app.data.radarr_data.edit_indexer_modal.is_some());
assert!(app.data.radarr_data.edit_indexer_modal.is_none());
assert!(app.should_refresh);
assert_eq!(
app.data.radarr_data.prompt_confirm_action,
Some(RadarrEvent::EditIndexer(None))
Some(RadarrEvent::EditIndexer(expected_edit_indexer_params))
);
}
@@ -1408,7 +1436,7 @@ mod tests {
use crate::models::servarr_data::radarr::radarr_data::EDIT_INDEXER_TORRENT_SELECTION_BLOCKS;
use crate::models::BlockSelectionState;
use crate::network::radarr_network::RadarrEvent;
use pretty_assertions::assert_str_eq;
use pretty_assertions::{assert_eq, assert_str_eq};
use super::*;
@@ -1709,7 +1737,32 @@ mod tests {
.radarr_data
.selected_block
.set_index(0, EDIT_INDEXER_TORRENT_SELECTION_BLOCKS.len() - 1);
app.data.radarr_data.edit_indexer_modal = Some(EditIndexerModal::default());
let edit_indexer_modal = EditIndexerModal {
name: "Test Update".into(),
enable_rss: Some(false),
enable_automatic_search: Some(false),
enable_interactive_search: Some(false),
url: "https://localhost:9696/1/".into(),
api_key: "test1234".into(),
seed_ratio: "1.3".into(),
tags: "usenet, testing".into(),
priority: 25,
};
app.data.radarr_data.edit_indexer_modal = Some(edit_indexer_modal);
app.data.radarr_data.indexers.set_items(vec![indexer()]);
let expected_edit_indexer_params = EditIndexerParams {
indexer_id: 1,
name: Some("Test Update".to_owned()),
enable_rss: Some(false),
enable_automatic_search: Some(false),
enable_interactive_search: Some(false),
url: Some("https://localhost:9696/1/".to_owned()),
api_key: Some("test1234".to_owned()),
seed_ratio: Some("1.3".to_owned()),
tag_input_string: Some("usenet, testing".to_owned()),
priority: Some(25),
..EditIndexerParams::default()
};
EditIndexerHandler::with(
DEFAULT_KEYBINDINGS.confirm.key,
@@ -1720,11 +1773,11 @@ mod tests {
.handle();
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
assert!(app.data.radarr_data.edit_indexer_modal.is_some());
assert!(app.data.radarr_data.edit_indexer_modal.is_none());
assert!(app.should_refresh);
assert_eq!(
app.data.radarr_data.prompt_confirm_action,
Some(RadarrEvent::EditIndexer(None))
Some(RadarrEvent::EditIndexer(expected_edit_indexer_params))
);
}
}
@@ -1740,6 +1793,48 @@ mod tests {
})
}
#[test]
fn test_build_edit_indexer_params() {
let mut app = App::default();
let edit_indexer_modal = EditIndexerModal {
name: "Test Update".into(),
enable_rss: Some(false),
enable_automatic_search: Some(false),
enable_interactive_search: Some(false),
url: "https://localhost:9696/1/".into(),
api_key: "test1234".into(),
seed_ratio: "1.3".into(),
tags: "usenet, testing".into(),
priority: 25,
};
app.data.radarr_data.edit_indexer_modal = Some(edit_indexer_modal);
app.data.radarr_data.indexers.set_items(vec![indexer()]);
let expected_edit_indexer_params = EditIndexerParams {
indexer_id: 1,
name: Some("Test Update".to_owned()),
enable_rss: Some(false),
enable_automatic_search: Some(false),
enable_interactive_search: Some(false),
url: Some("https://localhost:9696/1/".to_owned()),
api_key: Some("test1234".to_owned()),
seed_ratio: Some("1.3".to_owned()),
tag_input_string: Some("usenet, testing".to_owned()),
priority: Some(25),
..EditIndexerParams::default()
};
let edit_indexer_params = EditIndexerHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveRadarrBlock::EditIndexerPrompt,
None,
)
.build_edit_indexer_params();
assert_eq!(edit_indexer_params, expected_edit_indexer_params);
assert!(app.data.radarr_data.edit_indexer_modal.is_none());
}
#[test]
fn test_edit_indexer_handler_is_not_ready_when_loading() {
let mut app = App::default();
@@ -2,6 +2,7 @@ use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handlers::{handle_prompt_toggle, KeyEventHandler};
use crate::models::radarr_models::IndexerSettings;
use crate::models::servarr_data::radarr::radarr_data::{
ActiveRadarrBlock, INDEXER_SETTINGS_BLOCKS,
};
@@ -19,6 +20,14 @@ pub(super) struct IndexerSettingsHandler<'a, 'b> {
_context: Option<ActiveRadarrBlock>,
}
impl<'a, 'b> IndexerSettingsHandler<'a, 'b> {
fn build_edit_indexer_settings_body(&mut self) -> IndexerSettings {
let indexer_settings = self.app.data.radarr_data.indexer_settings.clone().unwrap();
self.app.data.radarr_data.indexer_settings = None;
indexer_settings
}
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexerSettingsHandler<'a, 'b> {
fn accepts(active_block: ActiveRadarrBlock) -> bool {
INDEXER_SETTINGS_BLOCKS.contains(&active_block)
@@ -166,12 +175,13 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexerSettingsHandl
ActiveRadarrBlock::AllIndexerSettingsPrompt => {
match self.app.data.radarr_data.selected_block.get_active_block() {
ActiveRadarrBlock::IndexerSettingsConfirmPrompt => {
let radarr_data = &mut self.app.data.radarr_data;
if radarr_data.prompt_confirm {
radarr_data.prompt_confirm_action = Some(RadarrEvent::EditAllIndexerSettings(None));
if self.app.data.radarr_data.prompt_confirm {
self.app.data.radarr_data.prompt_confirm_action = Some(
RadarrEvent::EditAllIndexerSettings(self.build_edit_indexer_settings_body()),
);
self.app.should_refresh = true;
} else {
radarr_data.indexer_settings = None;
self.app.data.radarr_data.indexer_settings = None;
}
self.app.pop_navigation_stack();
@@ -258,8 +268,9 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexerSettingsHandl
&& self.key == DEFAULT_KEYBINDINGS.confirm.key
{
self.app.data.radarr_data.prompt_confirm = true;
self.app.data.radarr_data.prompt_confirm_action =
Some(RadarrEvent::EditAllIndexerSettings(None));
self.app.data.radarr_data.prompt_confirm_action = Some(
RadarrEvent::EditAllIndexerSettings(self.build_edit_indexer_settings_body()),
);
self.app.should_refresh = true;
self.app.pop_navigation_stack();
@@ -1,11 +1,13 @@
#[cfg(test)]
mod tests {
use pretty_assertions::assert_eq;
use strum::IntoEnumIterator;
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handlers::radarr_handlers::indexers::edit_indexer_settings_handler::IndexerSettingsHandler;
use crate::handlers::radarr_handlers::radarr_handler_test_utils::utils::indexer_settings;
use crate::handlers::KeyEventHandler;
use crate::models::radarr_models::IndexerSettings;
use crate::models::servarr_data::radarr::radarr_data::{
@@ -472,7 +474,7 @@ mod tests {
.radarr_data
.selected_block
.set_index(0, INDEXER_SETTINGS_SELECTION_BLOCKS.len() - 1);
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
app.data.radarr_data.indexer_settings = Some(indexer_settings());
app.data.radarr_data.prompt_confirm = true;
IndexerSettingsHandler::with(
@@ -486,9 +488,9 @@ mod tests {
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
assert_eq!(
app.data.radarr_data.prompt_confirm_action,
Some(RadarrEvent::EditAllIndexerSettings(None))
Some(RadarrEvent::EditAllIndexerSettings(indexer_settings()))
);
assert!(app.data.radarr_data.indexer_settings.is_some());
assert!(app.data.radarr_data.indexer_settings.is_none());
assert!(app.should_refresh);
}
@@ -858,7 +860,7 @@ mod tests {
}
mod test_handle_key_char {
use pretty_assertions::assert_str_eq;
use pretty_assertions::{assert_eq, assert_str_eq};
use crate::{
models::{
@@ -937,7 +939,7 @@ mod tests {
.radarr_data
.selected_block
.set_index(0, INDEXER_SETTINGS_SELECTION_BLOCKS.len() - 1);
app.data.radarr_data.indexer_settings = Some(IndexerSettings::default());
app.data.radarr_data.indexer_settings = Some(indexer_settings());
IndexerSettingsHandler::with(
DEFAULT_KEYBINDINGS.confirm.key,
@@ -950,9 +952,9 @@ mod tests {
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
assert_eq!(
app.data.radarr_data.prompt_confirm_action,
Some(RadarrEvent::EditAllIndexerSettings(None))
Some(RadarrEvent::EditAllIndexerSettings(indexer_settings()))
);
assert!(app.data.radarr_data.indexer_settings.is_some());
assert!(app.data.radarr_data.indexer_settings.is_none());
assert!(app.should_refresh);
}
}
@@ -968,6 +970,23 @@ mod tests {
})
}
#[test]
fn test_build_edit_indexer_settings_body() {
let mut app = App::default();
app.data.radarr_data.indexer_settings = Some(indexer_settings());
let body = IndexerSettingsHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveRadarrBlock::AllIndexerSettingsPrompt,
None,
)
.build_edit_indexer_settings_body();
assert_eq!(body, indexer_settings());
assert!(app.data.radarr_data.indexer_settings.is_none());
}
#[test]
fn test_edit_indexer_settings_handler_not_ready_when_loading() {
let mut app = App::default();
@@ -1,5 +1,6 @@
#[cfg(test)]
mod tests {
use pretty_assertions::assert_eq;
use rstest::rstest;
use strum::IntoEnumIterator;
@@ -7,6 +8,7 @@ mod tests {
use crate::app::App;
use crate::event::Key;
use crate::handlers::radarr_handlers::indexers::IndexersHandler;
use crate::handlers::radarr_handlers::radarr_handler_test_utils::utils::indexer;
use crate::handlers::KeyEventHandler;
use crate::models::servarr_data::radarr::radarr_data::{
ActiveRadarrBlock, EDIT_INDEXER_BLOCKS, INDEXERS_BLOCKS, INDEXER_SETTINGS_BLOCKS,
@@ -123,17 +125,17 @@ mod tests {
}
mod test_handle_submit {
use crate::handlers::radarr_handlers::radarr_handler_test_utils::utils::indexer;
use crate::models::servarr_data::modals::EditIndexerModal;
use crate::models::servarr_data::radarr::radarr_data::{
RadarrData, EDIT_INDEXER_NZB_SELECTION_BLOCKS, EDIT_INDEXER_TORRENT_SELECTION_BLOCKS,
};
use crate::models::servarr_models::{Indexer, IndexerField};
use crate::network::radarr_network::RadarrEvent;
use bimap::BiMap;
use pretty_assertions::assert_eq;
use serde_json::{Number, Value};
use crate::network::radarr_network::RadarrEvent;
use super::*;
const SUBMIT_KEY: Key = DEFAULT_KEYBINDINGS.submit.key;
@@ -239,11 +241,7 @@ mod tests {
#[test]
fn test_delete_indexer_prompt_confirm_submit() {
let mut app = App::default();
app
.data
.radarr_data
.indexers
.set_items(vec![Indexer::default()]);
app.data.radarr_data.indexers.set_items(vec![indexer()]);
app.data.radarr_data.prompt_confirm = true;
app.push_navigation_stack(ActiveRadarrBlock::Indexers.into());
app.push_navigation_stack(ActiveRadarrBlock::DeleteIndexerPrompt.into());
@@ -259,7 +257,7 @@ mod tests {
assert!(app.data.radarr_data.prompt_confirm);
assert_eq!(
app.data.radarr_data.prompt_confirm_action,
Some(RadarrEvent::DeleteIndexer(None))
Some(RadarrEvent::DeleteIndexer(1))
);
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
}
@@ -348,13 +346,13 @@ mod tests {
mod test_handle_key_char {
use pretty_assertions::assert_eq;
use super::*;
use crate::handlers::radarr_handlers::radarr_handler_test_utils::utils::indexer;
use crate::{
models::servarr_data::radarr::radarr_data::INDEXER_SETTINGS_SELECTION_BLOCKS,
network::radarr_network::RadarrEvent,
};
use super::*;
#[test]
fn test_refresh_indexers_key() {
let mut app = App::default();
@@ -542,11 +540,7 @@ mod tests {
#[test]
fn test_delete_indexer_prompt_confirm() {
let mut app = App::default();
app
.data
.radarr_data
.indexers
.set_items(vec![Indexer::default()]);
app.data.radarr_data.indexers.set_items(vec![indexer()]);
app.push_navigation_stack(ActiveRadarrBlock::Indexers.into());
app.push_navigation_stack(ActiveRadarrBlock::DeleteIndexerPrompt.into());
@@ -561,7 +555,7 @@ mod tests {
assert!(app.data.radarr_data.prompt_confirm);
assert_eq!(
app.data.radarr_data.prompt_confirm_action,
Some(RadarrEvent::DeleteIndexer(None))
Some(RadarrEvent::DeleteIndexer(1))
);
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Indexers.into());
}
@@ -639,6 +633,22 @@ mod tests {
})
}
#[test]
fn test_extract_indexer_id() {
let mut app = App::default();
app.data.radarr_data.indexers.set_items(vec![indexer()]);
let indexer_id = IndexersHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveRadarrBlock::Indexers,
None,
)
.extract_indexer_id();
assert_eq!(indexer_id, 1);
}
#[test]
fn test_indexers_handler_not_ready_when_loading() {
let mut app = App::default();
+8 -2
View File
@@ -33,6 +33,10 @@ pub(super) struct IndexersHandler<'a, 'b> {
impl<'a, 'b> IndexersHandler<'a, 'b> {
handle_table_events!(self, indexers, self.app.data.radarr_data.indexers, Indexer);
fn extract_indexer_id(&self) -> i64 {
self.app.data.radarr_data.indexers.current_selection().id
}
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexersHandler<'a, 'b> {
@@ -115,9 +119,10 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexersHandler<'a,
fn handle_submit(&mut self) {
match self.active_radarr_block {
ActiveRadarrBlock::DeleteIndexerPrompt => {
let indexer_id = self.extract_indexer_id();
let radarr_data = &mut self.app.data.radarr_data;
if radarr_data.prompt_confirm {
radarr_data.prompt_confirm_action = Some(RadarrEvent::DeleteIndexer(None));
radarr_data.prompt_confirm_action = Some(RadarrEvent::DeleteIndexer(indexer_id));
}
self.app.pop_navigation_stack();
@@ -189,7 +194,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexersHandler<'a,
ActiveRadarrBlock::DeleteIndexerPrompt => {
if key == DEFAULT_KEYBINDINGS.confirm.key {
self.app.data.radarr_data.prompt_confirm = true;
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::DeleteIndexer(None));
self.app.data.radarr_data.prompt_confirm_action =
Some(RadarrEvent::DeleteIndexer(self.extract_indexer_id()));
self.app.pop_navigation_stack();
}
@@ -1,10 +1,14 @@
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::handlers::table_handler::TableHandlingConfig;
use crate::handlers::{handle_prompt_toggle, KeyEventHandler};
use crate::models::radarr_models::AddMovieSearchResult;
use crate::models::radarr_models::{
AddMovieBody, AddMovieOptions, AddMovieSearchResult, CollectionMovie,
};
use crate::models::servarr_data::radarr::modals::AddMovieModal;
use crate::models::servarr_data::radarr::radarr_data::{
ActiveRadarrBlock, ADD_MOVIE_BLOCKS, ADD_MOVIE_SELECTION_BLOCKS,
};
use crate::models::stateful_table::StatefulTable;
use crate::models::{BlockSelectionState, Scrollable};
use crate::network::radarr_network::RadarrEvent;
use crate::{handle_table_events, handle_text_box_keys, handle_text_box_left_right_keys, App, Key};
@@ -30,9 +34,95 @@ impl<'a, 'b> AddMovieHandler<'a, 'b> {
.radarr_data
.add_searched_movies
.as_mut()
.unwrap(),
.unwrap_or(&mut StatefulTable::default()),
AddMovieSearchResult
);
fn build_add_movie_body(&mut self) -> AddMovieBody {
let tags = self
.app
.data
.radarr_data
.add_movie_modal
.as_ref()
.unwrap()
.tags
.text
.clone();
let AddMovieModal {
root_folder_list,
monitor_list,
minimum_availability_list,
quality_profile_list,
..
} = self.app.data.radarr_data.add_movie_modal.as_ref().unwrap();
let (tmdb_id, title) = if let Some(context) = self.context {
if context == ActiveRadarrBlock::CollectionDetails {
let CollectionMovie { tmdb_id, title, .. } = self
.app
.data
.radarr_data
.collection_movies
.current_selection()
.clone();
(tmdb_id, title.text)
} else {
let AddMovieSearchResult { tmdb_id, title, .. } = self
.app
.data
.radarr_data
.add_searched_movies
.as_ref()
.unwrap()
.current_selection()
.clone();
(tmdb_id, title.text)
}
} else {
let AddMovieSearchResult { tmdb_id, title, .. } = self
.app
.data
.radarr_data
.add_searched_movies
.as_ref()
.unwrap()
.current_selection()
.clone();
(tmdb_id, title.text)
};
let quality_profile = quality_profile_list.current_selection();
let quality_profile_id = *self
.app
.data
.radarr_data
.quality_profile_map
.iter()
.filter(|(_, value)| *value == quality_profile)
.map(|(key, _)| key)
.next()
.unwrap();
let path = root_folder_list.current_selection().path.clone();
let monitor = monitor_list.current_selection().to_string();
let minimum_availability = minimum_availability_list.current_selection().to_string();
self.app.data.radarr_data.add_movie_modal = None;
AddMovieBody {
tmdb_id,
title,
root_folder_path: path,
minimum_availability,
monitored: true,
quality_profile_id,
tags: Vec::new(),
tag_input_string: Some(tags),
add_options: AddMovieOptions {
monitor,
search_for_movie: true,
},
}
}
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for AddMovieHandler<'a, 'b> {
@@ -361,7 +451,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for AddMovieHandler<'a,
match self.app.data.radarr_data.selected_block.get_active_block() {
ActiveRadarrBlock::AddMovieConfirmPrompt => {
if self.app.data.radarr_data.prompt_confirm {
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::AddMovie(None));
self.app.data.radarr_data.prompt_confirm_action =
Some(RadarrEvent::AddMovie(self.build_add_movie_body()));
}
self.app.pop_navigation_stack();
@@ -461,7 +552,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for AddMovieHandler<'a,
&& key == DEFAULT_KEYBINDINGS.confirm.key
{
self.app.data.radarr_data.prompt_confirm = true;
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::AddMovie(None));
self.app.data.radarr_data.prompt_confirm_action =
Some(RadarrEvent::AddMovie(self.build_add_movie_body()));
self.app.pop_navigation_stack();
}
}
@@ -1,17 +1,24 @@
#[cfg(test)]
mod tests {
use crate::handlers::radarr_handlers::radarr_handler_test_utils::utils::add_movie_search_result;
use crate::models::stateful_table::StatefulTable;
use pretty_assertions::assert_str_eq;
use rstest::rstest;
use strum::IntoEnumIterator;
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handlers::radarr_handlers::library::add_movie_handler::AddMovieHandler;
use crate::handlers::radarr_handlers::radarr_handler_test_utils::utils::add_movie_body;
use crate::handlers::radarr_handlers::radarr_handler_test_utils::utils::collection_movie;
use crate::handlers::KeyEventHandler;
use crate::models::radarr_models::{AddMovieSearchResult, MinimumAvailability, MovieMonitor};
use crate::models::servarr_data::radarr::modals::AddMovieModal;
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, ADD_MOVIE_BLOCKS};
use crate::models::servarr_models::RootFolder;
use crate::models::HorizontallyScrollableText;
use bimap::BiMap;
mod test_handle_scroll_up_and_down {
use pretty_assertions::assert_eq;
@@ -758,6 +765,9 @@ mod tests {
use pretty_assertions::{assert_eq, assert_str_eq};
use rstest::rstest;
use crate::handlers::radarr_handlers::radarr_handler_test_utils::utils::{
add_movie_body, add_movie_search_result, collection_movie,
};
use crate::models::radarr_models::Movie;
use crate::models::servarr_data::radarr::modals::AddMovieModal;
use crate::models::servarr_data::radarr::radarr_data::ADD_MOVIE_SELECTION_BLOCKS;
@@ -969,8 +979,10 @@ mod tests {
assert_eq!(app.data.radarr_data.prompt_confirm_action, None);
}
#[test]
fn test_add_movie_confirm_prompt_prompt_confirmation_submit() {
#[rstest]
fn test_add_movie_confirm_prompt_prompt_confirmation_submit(
#[values(true, false)] movie_details_context: bool,
) {
let mut app = App::default();
app.data.radarr_data.add_movie_modal = Some(AddMovieModal::default());
app.push_navigation_stack(ActiveRadarrBlock::Movies.into());
@@ -982,21 +994,67 @@ mod tests {
.radarr_data
.selected_block
.set_index(0, ADD_MOVIE_SELECTION_BLOCKS.len() - 1);
let mut add_movie_modal = AddMovieModal {
tags: "usenet, testing".into(),
..AddMovieModal::default()
};
add_movie_modal.root_folder_list.set_items(vec![
RootFolder {
id: 1,
path: "/nfs".to_owned(),
accessible: true,
free_space: 219902325555200,
unmapped_folders: None,
},
RootFolder {
id: 2,
path: "/nfs2".to_owned(),
accessible: true,
free_space: 21990232555520,
unmapped_folders: None,
},
]);
add_movie_modal.root_folder_list.state.select(Some(1));
add_movie_modal
.quality_profile_list
.set_items(vec!["HD - 1080p".to_owned()]);
add_movie_modal
.monitor_list
.set_items(Vec::from_iter(MovieMonitor::iter()));
add_movie_modal
.minimum_availability_list
.set_items(Vec::from_iter(MinimumAvailability::iter()));
app.data.radarr_data.add_movie_modal = Some(add_movie_modal);
app.data.radarr_data.quality_profile_map =
BiMap::from_iter([(2222, "HD - 1080p".to_owned())]);
let context = if movie_details_context {
app
.data
.radarr_data
.collection_movies
.set_items(vec![collection_movie()]);
Some(ActiveRadarrBlock::CollectionDetails)
} else {
let mut add_searched_movies = StatefulTable::default();
add_searched_movies.set_items(vec![add_movie_search_result()]);
app.data.radarr_data.add_searched_movies = Some(add_searched_movies);
None
};
AddMovieHandler::with(
SUBMIT_KEY,
&mut app,
ActiveRadarrBlock::AddMoviePrompt,
None,
context,
)
.handle();
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Movies.into());
assert_eq!(
app.data.radarr_data.prompt_confirm_action,
Some(RadarrEvent::AddMovie(None))
Some(RadarrEvent::AddMovie(add_movie_body()))
);
assert!(app.data.radarr_data.add_movie_modal.is_some());
assert!(app.data.radarr_data.add_movie_modal.is_none());
}
#[rstest]
@@ -1266,10 +1324,18 @@ mod tests {
}
mod test_handle_key_char {
use bimap::BiMap;
use pretty_assertions::assert_eq;
use rstest::rstest;
use super::*;
use crate::{
handlers::radarr_handlers::radarr_handler_test_utils::utils::{
add_movie_body, add_movie_search_result, collection_movie,
},
models::{
servarr_data::radarr::{modals::AddMovieModal, radarr_data::ADD_MOVIE_SELECTION_BLOCKS},
stateful_table::StatefulTable,
BlockSelectionState,
},
network::radarr_network::RadarrEvent,
@@ -1368,8 +1434,10 @@ mod tests {
);
}
#[test]
fn test_add_movie_confirm_prompt_prompt_confirmation_confirm() {
#[rstest]
fn test_add_movie_confirm_prompt_prompt_confirmation_confirm(
#[values(true, false)] movie_details_context: bool,
) {
let mut app = App::default();
app.data.radarr_data.add_movie_modal = Some(AddMovieModal::default());
app.push_navigation_stack(ActiveRadarrBlock::Movies.into());
@@ -1380,21 +1448,67 @@ mod tests {
.radarr_data
.selected_block
.set_index(0, ADD_MOVIE_SELECTION_BLOCKS.len() - 1);
let mut add_movie_modal = AddMovieModal {
tags: "usenet, testing".into(),
..AddMovieModal::default()
};
add_movie_modal.root_folder_list.set_items(vec![
RootFolder {
id: 1,
path: "/nfs".to_owned(),
accessible: true,
free_space: 219902325555200,
unmapped_folders: None,
},
RootFolder {
id: 2,
path: "/nfs2".to_owned(),
accessible: true,
free_space: 21990232555520,
unmapped_folders: None,
},
]);
add_movie_modal.root_folder_list.state.select(Some(1));
add_movie_modal
.quality_profile_list
.set_items(vec!["HD - 1080p".to_owned()]);
add_movie_modal
.monitor_list
.set_items(Vec::from_iter(MovieMonitor::iter()));
add_movie_modal
.minimum_availability_list
.set_items(Vec::from_iter(MinimumAvailability::iter()));
app.data.radarr_data.add_movie_modal = Some(add_movie_modal);
app.data.radarr_data.quality_profile_map =
BiMap::from_iter([(2222, "HD - 1080p".to_owned())]);
let context = if movie_details_context {
app
.data
.radarr_data
.collection_movies
.set_items(vec![collection_movie()]);
Some(ActiveRadarrBlock::CollectionDetails)
} else {
let mut add_searched_movies = StatefulTable::default();
add_searched_movies.set_items(vec![add_movie_search_result()]);
app.data.radarr_data.add_searched_movies = Some(add_searched_movies);
None
};
AddMovieHandler::with(
DEFAULT_KEYBINDINGS.confirm.key,
&mut app,
ActiveRadarrBlock::AddMoviePrompt,
None,
context,
)
.handle();
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Movies.into());
assert_eq!(
app.data.radarr_data.prompt_confirm_action,
Some(RadarrEvent::AddMovie(None))
Some(RadarrEvent::AddMovie(add_movie_body()))
);
assert!(app.data.radarr_data.add_movie_modal.is_some());
assert!(app.data.radarr_data.add_movie_modal.is_none());
}
}
@@ -1409,6 +1523,80 @@ mod tests {
});
}
#[test]
fn test_add_movie_search_no_panic_on_none_search_result() {
let mut app = App::default();
app.data.radarr_data.add_searched_movies = None;
AddMovieHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveRadarrBlock::AddMovieSearchResults,
None,
)
.handle();
}
#[rstest]
fn test_build_add_movie_body(#[values(true, false)] movie_details_context: bool) {
let mut app = App::default();
let mut add_movie_modal = AddMovieModal {
tags: "usenet, testing".into(),
..AddMovieModal::default()
};
add_movie_modal.root_folder_list.set_items(vec![
RootFolder {
id: 1,
path: "/nfs".to_owned(),
accessible: true,
free_space: 219902325555200,
unmapped_folders: None,
},
RootFolder {
id: 2,
path: "/nfs2".to_owned(),
accessible: true,
free_space: 21990232555520,
unmapped_folders: None,
},
]);
add_movie_modal.root_folder_list.state.select(Some(1));
add_movie_modal
.quality_profile_list
.set_items(vec!["HD - 1080p".to_owned()]);
add_movie_modal
.monitor_list
.set_items(Vec::from_iter(MovieMonitor::iter()));
add_movie_modal
.minimum_availability_list
.set_items(Vec::from_iter(MinimumAvailability::iter()));
app.data.radarr_data.add_movie_modal = Some(add_movie_modal);
app.data.radarr_data.quality_profile_map = BiMap::from_iter([(2222, "HD - 1080p".to_owned())]);
let context = if movie_details_context {
app
.data
.radarr_data
.collection_movies
.set_items(vec![collection_movie()]);
Some(ActiveRadarrBlock::CollectionDetails)
} else {
let mut add_searched_movies = StatefulTable::default();
add_searched_movies.set_items(vec![add_movie_search_result()]);
app.data.radarr_data.add_searched_movies = Some(add_searched_movies);
None
};
let actual_add_movie_body = AddMovieHandler::with(
DEFAULT_KEYBINDINGS.confirm.key,
&mut app,
ActiveRadarrBlock::AddMoviePrompt,
context,
)
.build_add_movie_body();
assert_eq!(actual_add_movie_body, add_movie_body());
}
#[test]
fn test_add_movie_handler_is_not_ready_when_loading() {
let mut app = App::default();
@@ -2,6 +2,7 @@ use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handlers::{handle_prompt_toggle, KeyEventHandler};
use crate::models::radarr_models::DeleteMovieParams;
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, DELETE_MOVIE_BLOCKS};
use crate::network::radarr_network::RadarrEvent;
@@ -16,6 +17,21 @@ pub(super) struct DeleteMovieHandler<'a, 'b> {
_context: Option<ActiveRadarrBlock>,
}
impl<'a, 'b> DeleteMovieHandler<'a, 'b> {
fn build_delete_movie_params(&mut self) -> DeleteMovieParams {
let id = self.app.data.radarr_data.movies.current_selection().id;
let delete_movie_files = self.app.data.radarr_data.delete_movie_files;
let add_list_exclusion = self.app.data.radarr_data.add_list_exclusion;
self.app.data.radarr_data.reset_delete_movie_preferences();
DeleteMovieParams {
id,
delete_movie_files,
add_list_exclusion,
}
}
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for DeleteMovieHandler<'a, 'b> {
fn accepts(active_block: ActiveRadarrBlock) -> bool {
DELETE_MOVIE_BLOCKS.contains(&active_block)
@@ -72,7 +88,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for DeleteMovieHandler<'
match self.app.data.radarr_data.selected_block.get_active_block() {
ActiveRadarrBlock::DeleteMovieConfirmPrompt => {
if self.app.data.radarr_data.prompt_confirm {
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::DeleteMovie(None));
self.app.data.radarr_data.prompt_confirm_action =
Some(RadarrEvent::DeleteMovie(self.build_delete_movie_params()));
self.app.should_refresh = true;
} else {
self.app.data.radarr_data.reset_delete_movie_preferences();
@@ -108,7 +125,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for DeleteMovieHandler<'
&& self.key == DEFAULT_KEYBINDINGS.confirm.key
{
self.app.data.radarr_data.prompt_confirm = true;
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::DeleteMovie(None));
self.app.data.radarr_data.prompt_confirm_action =
Some(RadarrEvent::DeleteMovie(self.build_delete_movie_params()));
self.app.should_refresh = true;
self.app.pop_navigation_stack();
@@ -1,12 +1,15 @@
#[cfg(test)]
mod tests {
use pretty_assertions::assert_eq;
use strum::IntoEnumIterator;
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handlers::radarr_handlers::library::delete_movie_handler::DeleteMovieHandler;
use crate::handlers::radarr_handlers::radarr_handler_test_utils::utils::movie;
use crate::handlers::KeyEventHandler;
use crate::models::radarr_models::DeleteMovieParams;
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, DELETE_MOVIE_BLOCKS};
mod test_handle_scroll_up_and_down {
@@ -119,8 +122,14 @@ mod tests {
#[test]
fn test_delete_movie_confirm_prompt_prompt_confirmation_submit() {
let mut app = App::default();
let expected_delete_movie_params = DeleteMovieParams {
id: 1,
delete_movie_files: true,
add_list_exclusion: true,
};
app.push_navigation_stack(ActiveRadarrBlock::Movies.into());
app.push_navigation_stack(ActiveRadarrBlock::DeleteMoviePrompt.into());
app.data.radarr_data.movies.set_items(vec![movie()]);
app.data.radarr_data.prompt_confirm = true;
app.data.radarr_data.delete_movie_files = true;
app.data.radarr_data.add_list_exclusion = true;
@@ -142,12 +151,12 @@ mod tests {
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Movies.into());
assert_eq!(
app.data.radarr_data.prompt_confirm_action,
Some(RadarrEvent::DeleteMovie(None))
Some(RadarrEvent::DeleteMovie(expected_delete_movie_params))
);
assert!(app.should_refresh);
assert!(app.data.radarr_data.prompt_confirm);
assert!(app.data.radarr_data.delete_movie_files);
assert!(app.data.radarr_data.add_list_exclusion);
assert!(!app.data.radarr_data.delete_movie_files);
assert!(!app.data.radarr_data.add_list_exclusion);
}
#[test]
@@ -212,6 +221,7 @@ mod tests {
mod test_handle_esc {
use super::*;
use pretty_assertions::assert_eq;
use rstest::rstest;
const ESC_KEY: Key = DEFAULT_KEYBINDINGS.esc.key;
@@ -248,14 +258,21 @@ mod tests {
},
network::radarr_network::RadarrEvent,
};
use pretty_assertions::assert_eq;
use super::*;
#[test]
fn test_delete_movie_confirm_prompt_prompt_confirm() {
let mut app = App::default();
let expected_delete_movie_params = DeleteMovieParams {
id: 1,
delete_movie_files: true,
add_list_exclusion: true,
};
app.push_navigation_stack(ActiveRadarrBlock::Movies.into());
app.push_navigation_stack(ActiveRadarrBlock::DeleteMoviePrompt.into());
app.data.radarr_data.movies.set_items(vec![movie()]);
app.data.radarr_data.delete_movie_files = true;
app.data.radarr_data.add_list_exclusion = true;
app.data.radarr_data.selected_block = BlockSelectionState::new(DELETE_MOVIE_SELECTION_BLOCKS);
@@ -276,12 +293,12 @@ mod tests {
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Movies.into());
assert_eq!(
app.data.radarr_data.prompt_confirm_action,
Some(RadarrEvent::DeleteMovie(None))
Some(RadarrEvent::DeleteMovie(expected_delete_movie_params))
);
assert!(app.should_refresh);
assert!(app.data.radarr_data.prompt_confirm);
assert!(app.data.radarr_data.delete_movie_files);
assert!(app.data.radarr_data.add_list_exclusion);
assert!(!app.data.radarr_data.delete_movie_files);
assert!(!app.data.radarr_data.add_list_exclusion);
}
}
@@ -296,6 +313,31 @@ mod tests {
});
}
#[test]
fn test_build_delete_movie_params() {
let mut app = App::default();
app.data.radarr_data.movies.set_items(vec![movie()]);
app.data.radarr_data.delete_movie_files = true;
app.data.radarr_data.add_list_exclusion = true;
let expected_delete_movie_params = DeleteMovieParams {
id: 1,
delete_movie_files: true,
add_list_exclusion: true,
};
let delete_movie_params = DeleteMovieHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveRadarrBlock::DeleteMoviePrompt,
None,
)
.build_delete_movie_params();
assert_eq!(delete_movie_params, expected_delete_movie_params);
assert!(!app.data.radarr_data.delete_movie_files);
assert!(!app.data.radarr_data.add_list_exclusion);
}
#[test]
fn test_delete_movie_handler_not_ready_when_loading() {
let mut app = App::default();
@@ -2,6 +2,8 @@ use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handlers::{handle_prompt_toggle, KeyEventHandler};
use crate::models::radarr_models::EditMovieParams;
use crate::models::servarr_data::radarr::modals::EditMovieModal;
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, EDIT_MOVIE_BLOCKS};
use crate::models::Scrollable;
use crate::network::radarr_network::RadarrEvent;
@@ -18,6 +20,57 @@ pub(super) struct EditMovieHandler<'a, 'b> {
context: Option<ActiveRadarrBlock>,
}
impl<'a, 'b> EditMovieHandler<'a, 'b> {
fn build_edit_movie_params(&mut self) -> EditMovieParams {
let movie_id = self.app.data.radarr_data.movies.current_selection().id;
let tags = self
.app
.data
.radarr_data
.edit_movie_modal
.as_ref()
.unwrap()
.tags
.text
.clone();
let params = {
let EditMovieModal {
monitored,
path,
minimum_availability_list,
quality_profile_list,
..
} = self.app.data.radarr_data.edit_movie_modal.as_ref().unwrap();
let quality_profile = quality_profile_list.current_selection();
let quality_profile_id = *self
.app
.data
.radarr_data
.quality_profile_map
.iter()
.filter(|(_, value)| *value == quality_profile)
.map(|(key, _)| key)
.next()
.unwrap();
EditMovieParams {
movie_id,
monitored: *monitored,
minimum_availability: Some(*minimum_availability_list.current_selection()),
quality_profile_id: Some(quality_profile_id),
root_folder_path: Some(path.text.clone()),
tags: None,
tag_input_string: Some(tags),
clear_tags: false,
}
};
self.app.data.radarr_data.edit_movie_modal = None;
params
}
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditMovieHandler<'a, 'b> {
fn accepts(active_block: ActiveRadarrBlock) -> bool {
EDIT_MOVIE_BLOCKS.contains(&active_block)
@@ -222,7 +275,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditMovieHandler<'a,
match self.app.data.radarr_data.selected_block.get_active_block() {
ActiveRadarrBlock::EditMovieConfirmPrompt => {
if self.app.data.radarr_data.prompt_confirm {
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::EditMovie(None));
self.app.data.radarr_data.prompt_confirm_action =
Some(RadarrEvent::EditMovie(self.build_edit_movie_params()));
self.app.should_refresh = true;
}
@@ -333,7 +387,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditMovieHandler<'a,
&& key == DEFAULT_KEYBINDINGS.confirm.key
{
self.app.data.radarr_data.prompt_confirm = true;
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::EditMovie(None));
self.app.data.radarr_data.prompt_confirm_action =
Some(RadarrEvent::EditMovie(self.build_edit_movie_params()));
self.app.should_refresh = true;
self.app.pop_navigation_stack();
@@ -1,5 +1,6 @@
#[cfg(test)]
mod tests {
use bimap::BiMap;
use pretty_assertions::assert_str_eq;
use strum::IntoEnumIterator;
@@ -7,8 +8,9 @@ mod tests {
use crate::app::App;
use crate::event::Key;
use crate::handlers::radarr_handlers::library::edit_movie_handler::EditMovieHandler;
use crate::handlers::radarr_handlers::radarr_handler_test_utils::utils::movie;
use crate::handlers::KeyEventHandler;
use crate::models::radarr_models::MinimumAvailability;
use crate::models::radarr_models::{EditMovieParams, MinimumAvailability, Movie};
use crate::models::servarr_data::radarr::modals::EditMovieModal;
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, EDIT_MOVIE_BLOCKS};
@@ -641,7 +643,34 @@ mod tests {
#[test]
fn test_edit_movie_confirm_prompt_prompt_confirmation_submit() {
let mut app = App::default();
app.data.radarr_data.edit_movie_modal = Some(EditMovieModal::default());
let mut edit_movie = EditMovieModal {
tags: "usenet, testing".to_owned().into(),
path: "/nfs/Test Path".to_owned().into(),
monitored: Some(false),
..EditMovieModal::default()
};
edit_movie
.quality_profile_list
.set_items(vec!["Any".to_owned(), "HD - 1080p".to_owned()]);
edit_movie
.minimum_availability_list
.set_items(Vec::from_iter(MinimumAvailability::iter()));
app.data.radarr_data.edit_movie_modal = Some(edit_movie);
app.data.radarr_data.movies.set_items(vec![Movie {
monitored: false,
..movie()
}]);
app.data.radarr_data.quality_profile_map =
BiMap::from_iter([(1111, "Any".to_owned()), (2222, "HD - 1080p".to_owned())]);
let expected_edit_movie_params = EditMovieParams {
movie_id: 1,
monitored: Some(false),
minimum_availability: Some(MinimumAvailability::Announced),
quality_profile_id: Some(1111),
root_folder_path: Some("/nfs/Test Path".to_owned()),
tag_input_string: Some("usenet, testing".into()),
..EditMovieParams::default()
};
app.push_navigation_stack(ActiveRadarrBlock::Movies.into());
app.push_navigation_stack(ActiveRadarrBlock::EditMoviePrompt.into());
app.data.radarr_data.prompt_confirm = true;
@@ -663,9 +692,9 @@ mod tests {
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Movies.into());
assert_eq!(
app.data.radarr_data.prompt_confirm_action,
Some(RadarrEvent::EditMovie(None))
Some(RadarrEvent::EditMovie(expected_edit_movie_params))
);
assert!(app.data.radarr_data.edit_movie_modal.is_some());
assert!(app.data.radarr_data.edit_movie_modal.is_none());
assert!(app.should_refresh);
}
@@ -1053,7 +1082,34 @@ mod tests {
#[test]
fn test_edit_movie_confirm_prompt_prompt_confirm() {
let mut app = App::default();
app.data.radarr_data.edit_movie_modal = Some(EditMovieModal::default());
let mut edit_movie = EditMovieModal {
tags: "usenet, testing".to_owned().into(),
path: "/nfs/Test Path".to_owned().into(),
monitored: Some(false),
..EditMovieModal::default()
};
edit_movie
.quality_profile_list
.set_items(vec!["Any".to_owned(), "HD - 1080p".to_owned()]);
edit_movie
.minimum_availability_list
.set_items(Vec::from_iter(MinimumAvailability::iter()));
app.data.radarr_data.edit_movie_modal = Some(edit_movie);
app.data.radarr_data.movies.set_items(vec![Movie {
monitored: false,
..movie()
}]);
app.data.radarr_data.quality_profile_map =
BiMap::from_iter([(1111, "Any".to_owned()), (2222, "HD - 1080p".to_owned())]);
let expected_edit_movie_params = EditMovieParams {
movie_id: 1,
monitored: Some(false),
minimum_availability: Some(MinimumAvailability::Announced),
quality_profile_id: Some(1111),
root_folder_path: Some("/nfs/Test Path".to_owned()),
tag_input_string: Some("usenet, testing".into()),
..EditMovieParams::default()
};
app.push_navigation_stack(ActiveRadarrBlock::Movies.into());
app.push_navigation_stack(ActiveRadarrBlock::EditMoviePrompt.into());
app.data.radarr_data.selected_block = BlockSelectionState::new(EDIT_MOVIE_SELECTION_BLOCKS);
@@ -1074,9 +1130,9 @@ mod tests {
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Movies.into());
assert_eq!(
app.data.radarr_data.prompt_confirm_action,
Some(RadarrEvent::EditMovie(None))
Some(RadarrEvent::EditMovie(expected_edit_movie_params))
);
assert!(app.data.radarr_data.edit_movie_modal.is_some());
assert!(app.data.radarr_data.edit_movie_modal.is_none());
assert!(app.should_refresh);
}
}
@@ -1092,6 +1148,50 @@ mod tests {
});
}
#[test]
fn test_build_edit_movie_params() {
let mut app = App::default();
let mut edit_movie = EditMovieModal {
tags: "usenet, testing".to_owned().into(),
path: "/nfs/Test Path".to_owned().into(),
monitored: Some(false),
..EditMovieModal::default()
};
edit_movie
.quality_profile_list
.set_items(vec!["Any".to_owned(), "HD - 1080p".to_owned()]);
edit_movie
.minimum_availability_list
.set_items(Vec::from_iter(MinimumAvailability::iter()));
app.data.radarr_data.edit_movie_modal = Some(edit_movie);
app.data.radarr_data.movies.set_items(vec![Movie {
monitored: false,
..movie()
}]);
app.data.radarr_data.quality_profile_map =
BiMap::from_iter([(1111, "Any".to_owned()), (2222, "HD - 1080p".to_owned())]);
let expected_edit_movie_params = EditMovieParams {
movie_id: 1,
monitored: Some(false),
minimum_availability: Some(MinimumAvailability::Announced),
quality_profile_id: Some(1111),
root_folder_path: Some("/nfs/Test Path".to_owned()),
tag_input_string: Some("usenet, testing".into()),
..EditMovieParams::default()
};
let edit_movie_params = EditMovieHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveRadarrBlock::EditMoviePrompt,
None,
)
.build_edit_movie_params();
assert_eq!(edit_movie_params, expected_edit_movie_params);
assert!(app.data.radarr_data.edit_movie_modal.is_none());
}
#[test]
fn test_edit_movie_handler_is_not_ready_when_loading() {
let mut app = App::default();
@@ -6,7 +6,9 @@ use crate::event::Key;
use crate::handle_table_events;
use crate::handlers::table_handler::TableHandlingConfig;
use crate::handlers::{handle_prompt_toggle, KeyEventHandler};
use crate::models::radarr_models::{Credit, MovieHistoryItem, RadarrRelease};
use crate::models::radarr_models::{
Credit, MovieHistoryItem, RadarrRelease, RadarrReleaseDownloadBody,
};
use crate::models::servarr_data::radarr::radarr_data::{
ActiveRadarrBlock, EDIT_MOVIE_SELECTION_BLOCKS, MOVIE_DETAILS_BLOCKS,
};
@@ -79,6 +81,35 @@ impl<'a, 'b> MovieDetailsHandler<'a, 'b> {
.movie_crew,
Credit
);
fn build_radarr_release_download_body(&self) -> RadarrReleaseDownloadBody {
let movie_id = self.app.data.radarr_data.movies.current_selection().id;
let (guid, indexer_id) = {
let RadarrRelease {
guid, indexer_id, ..
} = self
.app
.data
.radarr_data
.movie_details_modal
.as_ref()
.unwrap()
.movie_releases
.current_selection();
(guid.clone(), *indexer_id)
};
RadarrReleaseDownloadBody {
guid,
indexer_id,
movie_id,
}
}
fn extract_movie_id(&self) -> i64 {
self.app.data.radarr_data.movies.current_selection().id
}
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for MovieDetailsHandler<'a, 'b> {
@@ -240,14 +271,15 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for MovieDetailsHandler<
ActiveRadarrBlock::AutomaticallySearchMoviePrompt => {
if self.app.data.radarr_data.prompt_confirm {
self.app.data.radarr_data.prompt_confirm_action =
Some(RadarrEvent::TriggerAutomaticSearch(None));
Some(RadarrEvent::TriggerAutomaticSearch(self.extract_movie_id()));
}
self.app.pop_navigation_stack();
}
ActiveRadarrBlock::UpdateAndScanPrompt => {
if self.app.data.radarr_data.prompt_confirm {
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::UpdateAndScan(None));
self.app.data.radarr_data.prompt_confirm_action =
Some(RadarrEvent::UpdateAndScan(self.extract_movie_id()));
}
self.app.pop_navigation_stack();
@@ -259,8 +291,9 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for MovieDetailsHandler<
}
ActiveRadarrBlock::ManualSearchConfirmPrompt => {
if self.app.data.radarr_data.prompt_confirm {
self.app.data.radarr_data.prompt_confirm_action =
Some(RadarrEvent::DownloadRelease(None));
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::DownloadRelease(
self.build_radarr_release_download_body(),
));
}
self.app.pop_navigation_stack();
@@ -333,19 +366,22 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for MovieDetailsHandler<
{
self.app.data.radarr_data.prompt_confirm = true;
self.app.data.radarr_data.prompt_confirm_action =
Some(RadarrEvent::TriggerAutomaticSearch(None));
Some(RadarrEvent::TriggerAutomaticSearch(self.extract_movie_id()));
self.app.pop_navigation_stack();
}
ActiveRadarrBlock::UpdateAndScanPrompt if key == DEFAULT_KEYBINDINGS.confirm.key => {
self.app.data.radarr_data.prompt_confirm = true;
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::UpdateAndScan(None));
self.app.data.radarr_data.prompt_confirm_action =
Some(RadarrEvent::UpdateAndScan(self.extract_movie_id()));
self.app.pop_navigation_stack();
}
ActiveRadarrBlock::ManualSearchConfirmPrompt if key == DEFAULT_KEYBINDINGS.confirm.key => {
self.app.data.radarr_data.prompt_confirm = true;
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::DownloadRelease(None));
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::DownloadRelease(
self.build_radarr_release_download_body(),
));
self.app.pop_navigation_stack();
}
@@ -2,7 +2,7 @@
mod tests {
use std::cmp::Ordering;
use pretty_assertions::assert_str_eq;
use pretty_assertions::{assert_eq, assert_str_eq};
use rstest::rstest;
use serde_json::Number;
use strum::IntoEnumIterator;
@@ -13,9 +13,10 @@ mod tests {
use crate::handlers::radarr_handlers::library::movie_details_handler::{
releases_sorting_options, MovieDetailsHandler,
};
use crate::handlers::radarr_handlers::radarr_handler_test_utils::utils::{movie, release};
use crate::handlers::KeyEventHandler;
use crate::models::radarr_models::RadarrRelease;
use crate::models::radarr_models::{Credit, MovieHistoryItem};
use crate::models::radarr_models::{RadarrRelease, RadarrReleaseDownloadBody};
use crate::models::servarr_data::radarr::modals::MovieDetailsModal;
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, MOVIE_DETAILS_BLOCKS};
use crate::models::servarr_models::{Language, Quality, QualityWrapper};
@@ -130,6 +131,7 @@ mod tests {
mod test_handle_home_end {
use crate::models::servarr_data::radarr::modals::MovieDetailsModal;
use pretty_assertions::assert_eq;
use super::*;
@@ -359,25 +361,31 @@ mod tests {
#[rstest]
#[case(
ActiveRadarrBlock::AutomaticallySearchMoviePrompt,
RadarrEvent::TriggerAutomaticSearch(None)
)]
#[case(
ActiveRadarrBlock::UpdateAndScanPrompt,
RadarrEvent::UpdateAndScan(None)
RadarrEvent::TriggerAutomaticSearch(1)
)]
#[case(ActiveRadarrBlock::UpdateAndScanPrompt, RadarrEvent::UpdateAndScan(1))]
#[case(
ActiveRadarrBlock::ManualSearchConfirmPrompt,
RadarrEvent::DownloadRelease(None)
RadarrEvent::DownloadRelease(RadarrReleaseDownloadBody {
guid: "1234".to_owned(),
indexer_id: 2,
movie_id: 1,
})
)]
fn test_movie_info_prompt_confirm_submit(
#[case] prompt_block: ActiveRadarrBlock,
#[case] expected_action: RadarrEvent,
) {
let mut app = App::default();
app.data.radarr_data.movie_details_modal = Some(MovieDetailsModal {
let mut movie_details_modal = MovieDetailsModal {
movie_details: ScrollableText::with_string("test".to_owned()),
..MovieDetailsModal::default()
});
};
movie_details_modal
.movie_releases
.set_items(vec![release()]);
app.data.radarr_data.movie_details_modal = Some(movie_details_modal);
app.data.radarr_data.movies.set_items(vec![movie()]);
app.data.radarr_data.prompt_confirm = true;
app.push_navigation_stack(ActiveRadarrBlock::MovieDetails.into());
app.push_navigation_stack(prompt_block.into());
@@ -771,25 +779,32 @@ mod tests {
#[rstest]
#[case(
ActiveRadarrBlock::AutomaticallySearchMoviePrompt,
RadarrEvent::TriggerAutomaticSearch(None)
)]
#[case(
ActiveRadarrBlock::UpdateAndScanPrompt,
RadarrEvent::UpdateAndScan(None)
RadarrEvent::TriggerAutomaticSearch(1)
)]
#[case(ActiveRadarrBlock::UpdateAndScanPrompt, RadarrEvent::UpdateAndScan(1))]
#[case(
ActiveRadarrBlock::ManualSearchConfirmPrompt,
RadarrEvent::DownloadRelease(None)
RadarrEvent::DownloadRelease(RadarrReleaseDownloadBody {
guid: "1234".to_owned(),
indexer_id: 2,
movie_id: 1,
})
)]
fn test_movie_info_prompt_confirm(
#[case] prompt_block: ActiveRadarrBlock,
#[case] expected_action: RadarrEvent,
) {
let mut app = App::default();
app.data.radarr_data.movie_details_modal = Some(MovieDetailsModal {
let mut movie_details_modal = MovieDetailsModal {
movie_details: ScrollableText::with_string("test".to_owned()),
..MovieDetailsModal::default()
});
};
movie_details_modal
.movie_releases
.set_items(vec![release()]);
app.data.radarr_data.movie_details_modal = Some(movie_details_modal);
app.data.radarr_data.movies.set_items(vec![movie()]);
app.data.radarr_data.prompt_confirm = true;
app.push_navigation_stack(ActiveRadarrBlock::MovieDetails.into());
app.push_navigation_stack(prompt_block.into());
@@ -813,6 +828,48 @@ mod tests {
}
}
#[test]
fn test_build_radarr_release_download_body() {
let mut app = App::default();
let mut movie_details_modal = MovieDetailsModal::default();
movie_details_modal
.movie_releases
.set_items(vec![release()]);
app.data.radarr_data.movie_details_modal = Some(movie_details_modal);
app.data.radarr_data.movies.set_items(vec![movie()]);
let expected_body = RadarrReleaseDownloadBody {
guid: "1234".to_owned(),
indexer_id: 2,
movie_id: 1,
};
let body = MovieDetailsHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveRadarrBlock::ManualSearchConfirmPrompt,
None,
)
.build_radarr_release_download_body();
assert_eq!(body, expected_body);
}
#[test]
fn test_extract_movie_id() {
let mut app = App::default();
app.data.radarr_data.movies.set_items(vec![movie()]);
let movie_id = MovieDetailsHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveRadarrBlock::AutomaticallySearchMoviePrompt,
None,
)
.extract_movie_id();
assert_eq!(movie_id, 1);
}
#[test]
fn test_releases_sorting_options_source() {
let expected_cmp_fn: fn(&RadarrRelease, &RadarrRelease) -> Ordering =
@@ -1,6 +1,18 @@
#[cfg(test)]
#[macro_use]
mod utils {
pub(in crate::handlers::radarr_handlers) mod utils {
use crate::models::radarr_models::{
AddMovieBody, AddMovieOptions, AddMovieSearchResult, Collection, CollectionMovie,
DownloadRecord, IndexerSettings, MediaInfo, MinimumAvailability, Movie, MovieCollection,
MovieFile, RadarrRelease, Rating, RatingsList,
};
use crate::models::servarr_models::{
Indexer, IndexerField, Language, Quality, QualityWrapper, RootFolder,
};
use crate::models::HorizontallyScrollableText;
use chrono::DateTime;
use serde_json::{json, Number};
#[macro_export]
macro_rules! test_edit_movie_key {
($handler:ident, $block:expr, $context:expr) => {
@@ -228,4 +240,245 @@ mod utils {
);
};
}
pub fn language() -> Language {
Language {
id: 1,
name: "English".to_owned(),
}
}
pub fn genres() -> Vec<String> {
vec!["cool".to_owned(), "family".to_owned(), "fun".to_owned()]
}
pub fn rating() -> Rating {
Rating {
value: Number::from_f64(9.9).unwrap(),
}
}
pub fn ratings_list() -> RatingsList {
RatingsList {
imdb: Some(rating()),
tmdb: Some(rating()),
rotten_tomatoes: Some(rating()),
}
}
pub fn media_info() -> MediaInfo {
MediaInfo {
audio_bitrate: 0,
audio_channels: Number::from_f64(7.1).unwrap(),
audio_codec: Some("AAC".to_owned()),
audio_languages: Some("eng".to_owned()),
audio_stream_count: 1,
video_bit_depth: 10,
video_bitrate: 0,
video_codec: "x265".to_owned(),
video_fps: Number::from_f64(23.976).unwrap(),
resolution: "1920x804".to_owned(),
run_time: "2:00:00".to_owned(),
scan_type: "Progressive".to_owned(),
}
}
pub fn movie_file() -> MovieFile {
MovieFile {
relative_path: "Test.mkv".to_owned(),
path: "/nfs/movies/Test.mkv".to_owned(),
date_added: DateTime::from(DateTime::parse_from_rfc3339("2022-12-30T07:37:56Z").unwrap()),
media_info: Some(media_info()),
}
}
pub fn collection_movie() -> CollectionMovie {
CollectionMovie {
title: "Test".to_owned().into(),
overview: "Collection blah blah blah".to_owned(),
year: 2023,
runtime: 120,
tmdb_id: 1234,
genres: genres(),
ratings: ratings_list(),
}
}
pub fn collection() -> Collection {
Collection {
id: 123,
title: "Test Collection".to_owned().into(),
root_folder_path: Some("/nfs/movies".to_owned()),
search_on_add: true,
monitored: true,
minimum_availability: MinimumAvailability::Released,
overview: Some("Collection blah blah blah".to_owned()),
quality_profile_id: 2222,
movies: Some(vec![collection_movie()]),
}
}
pub fn movie() -> Movie {
Movie {
id: 1,
title: "Test".to_owned().into(),
original_language: language(),
size_on_disk: 3543348019,
status: "Downloaded".to_owned(),
overview: "Blah blah blah".to_owned(),
path: "/nfs/movies".to_owned(),
studio: "21st Century Alex".to_owned(),
genres: genres(),
year: 2023,
monitored: true,
has_file: true,
runtime: 120,
tmdb_id: 1234,
quality_profile_id: 2222,
minimum_availability: MinimumAvailability::Announced,
certification: Some("R".to_owned()),
tags: vec![Number::from(1)],
ratings: ratings_list(),
movie_file: Some(movie_file()),
collection: Some(movie_collection()),
}
}
pub fn movie_collection() -> MovieCollection {
MovieCollection {
title: Some("Test Collection".to_owned()),
}
}
pub fn rejections() -> Vec<String> {
vec![
"Unknown quality profile".to_owned(),
"Release is already mapped".to_owned(),
]
}
pub fn quality() -> Quality {
Quality {
name: "HD - 1080p".to_owned(),
}
}
pub fn quality_wrapper() -> QualityWrapper {
QualityWrapper { quality: quality() }
}
pub fn release() -> RadarrRelease {
RadarrRelease {
guid: "1234".to_owned(),
protocol: "torrent".to_owned(),
age: 1,
title: HorizontallyScrollableText::from("Test Release"),
indexer: "kickass torrents".to_owned(),
indexer_id: 2,
size: 1234,
rejected: true,
rejections: Some(rejections()),
seeders: Some(Number::from(2)),
leechers: Some(Number::from(1)),
languages: Some(vec![language()]),
quality: quality_wrapper(),
}
}
pub fn add_movie_search_result() -> AddMovieSearchResult {
AddMovieSearchResult {
tmdb_id: 1234,
title: HorizontallyScrollableText::from("Test"),
original_language: language(),
status: "released".to_owned(),
overview: "New movie blah blah blah".to_owned(),
genres: genres(),
year: 2023,
runtime: 120,
ratings: ratings_list(),
}
}
pub fn download_record() -> DownloadRecord {
DownloadRecord {
title: "Test Download Title".to_owned(),
status: "downloading".to_owned(),
id: 1,
movie_id: 1,
size: 3543348019,
sizeleft: 1771674009,
output_path: Some(HorizontallyScrollableText::from("/nfs/movies/Test")),
indexer: "kickass torrents".to_owned(),
download_client: "transmission".to_owned(),
}
}
pub fn root_folder() -> RootFolder {
RootFolder {
id: 1,
path: "/nfs".to_owned(),
accessible: true,
free_space: 219902325555200,
unmapped_folders: None,
}
}
pub fn indexer() -> Indexer {
Indexer {
enable_rss: true,
enable_automatic_search: true,
enable_interactive_search: true,
supports_rss: true,
supports_search: true,
protocol: "torrent".to_owned(),
priority: 25,
download_client_id: 0,
name: Some("Test Indexer".to_owned()),
implementation_name: Some("Torznab".to_owned()),
implementation: Some("Torznab".to_owned()),
config_contract: Some("TorznabSettings".to_owned()),
tags: vec![Number::from(1)],
id: 1,
fields: Some(vec![
IndexerField {
name: Some("baseUrl".to_owned()),
value: Some(json!("https://test.com")),
},
IndexerField {
name: Some("apiKey".to_owned()),
value: Some(json!("")),
},
IndexerField {
name: Some("seedCriteria.seedRatio".to_owned()),
value: Some(json!("1.2")),
},
]),
}
}
pub fn indexer_settings() -> IndexerSettings {
IndexerSettings {
rss_sync_interval: 60,
allow_hardcoded_subs: true,
id: 1,
..IndexerSettings::default()
}
}
pub fn add_movie_body() -> AddMovieBody {
AddMovieBody {
tmdb_id: 1234,
title: "Test".to_owned(),
root_folder_path: "/nfs2".to_owned(),
minimum_availability: "announced".to_owned(),
monitored: true,
quality_profile_id: 2222,
tags: Vec::new(),
tag_input_string: Some("usenet, testing".into()),
add_options: AddMovieOptions {
monitor: "movieOnly".to_owned(),
search_for_movie: true,
},
}
}
}
@@ -5,7 +5,7 @@ use crate::handlers::radarr_handlers::handle_change_tab_left_right_keys;
use crate::handlers::table_handler::TableHandlingConfig;
use crate::handlers::{handle_clear_errors, handle_prompt_toggle, KeyEventHandler};
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, ROOT_FOLDERS_BLOCKS};
use crate::models::servarr_models::RootFolder;
use crate::models::servarr_models::{AddRootFolderBody, RootFolder};
use crate::models::HorizontallyScrollableText;
use crate::network::radarr_network::RadarrEvent;
use crate::{handle_table_events, handle_text_box_keys, handle_text_box_left_right_keys};
@@ -28,6 +28,32 @@ impl<'a, 'b> RootFoldersHandler<'a, 'b> {
self.app.data.radarr_data.root_folders,
RootFolder
);
fn build_add_root_folder_body(&mut self) -> AddRootFolderBody {
let path = self
.app
.data
.radarr_data
.edit_root_folder
.as_ref()
.unwrap()
.text
.clone();
self.app.data.radarr_data.edit_root_folder = None;
AddRootFolderBody { path }
}
fn extract_root_folder_id(&mut self) -> i64 {
self
.app
.data
.radarr_data
.root_folders
.current_selection()
.id
}
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for RootFoldersHandler<'a, 'b> {
@@ -124,7 +150,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for RootFoldersHandler<'
ActiveRadarrBlock::DeleteRootFolderPrompt => {
if self.app.data.radarr_data.prompt_confirm {
self.app.data.radarr_data.prompt_confirm_action =
Some(RadarrEvent::DeleteRootFolder(None));
Some(RadarrEvent::DeleteRootFolder(self.extract_root_folder_id()));
}
self.app.pop_navigation_stack();
@@ -140,7 +166,9 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for RootFoldersHandler<'
.text
.is_empty() =>
{
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::AddRootFolder(None));
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::AddRootFolder(
self.build_add_root_folder_body(),
));
self.app.data.radarr_data.prompt_confirm = true;
self.app.should_ignore_quit_key = false;
self.app.pop_navigation_stack();
@@ -192,7 +220,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for RootFoldersHandler<'
if key == DEFAULT_KEYBINDINGS.confirm.key {
self.app.data.radarr_data.prompt_confirm = true;
self.app.data.radarr_data.prompt_confirm_action =
Some(RadarrEvent::DeleteRootFolder(None));
Some(RadarrEvent::DeleteRootFolder(self.extract_root_folder_id()));
self.app.pop_navigation_stack();
}
@@ -1,14 +1,16 @@
#[cfg(test)]
mod tests {
use pretty_assertions::assert_eq;
use strum::IntoEnumIterator;
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handlers::radarr_handlers::radarr_handler_test_utils::utils::root_folder;
use crate::handlers::radarr_handlers::root_folders::RootFoldersHandler;
use crate::handlers::KeyEventHandler;
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, ROOT_FOLDERS_BLOCKS};
use crate::models::servarr_models::RootFolder;
use crate::models::servarr_models::{AddRootFolderBody, RootFolder};
use crate::models::HorizontallyScrollableText;
mod test_handle_home_end {
@@ -250,6 +252,9 @@ mod tests {
#[test]
fn test_add_root_folder_prompt_confirm_submit() {
let mut app = App::default();
let expected_add_root_folder_body = AddRootFolderBody {
path: "Test".to_owned(),
};
app
.data
.radarr_data
@@ -273,7 +278,7 @@ mod tests {
assert!(!app.should_ignore_quit_key);
assert_eq!(
app.data.radarr_data.prompt_confirm_action,
Some(RadarrEvent::AddRootFolder(None))
Some(RadarrEvent::AddRootFolder(expected_add_root_folder_body))
);
assert_eq!(
app.get_current_route(),
@@ -314,7 +319,7 @@ mod tests {
.data
.radarr_data
.root_folders
.set_items(vec![RootFolder::default()]);
.set_items(vec![root_folder()]);
app.data.radarr_data.prompt_confirm = true;
app.push_navigation_stack(ActiveRadarrBlock::RootFolders.into());
app.push_navigation_stack(ActiveRadarrBlock::DeleteRootFolderPrompt.into());
@@ -330,7 +335,7 @@ mod tests {
assert!(app.data.radarr_data.prompt_confirm);
assert_eq!(
app.data.radarr_data.prompt_confirm_action,
Some(RadarrEvent::DeleteRootFolder(None))
Some(RadarrEvent::DeleteRootFolder(1))
);
assert_eq!(
app.get_current_route(),
@@ -604,7 +609,7 @@ mod tests {
.data
.radarr_data
.root_folders
.set_items(vec![RootFolder::default()]);
.set_items(vec![root_folder()]);
app.push_navigation_stack(ActiveRadarrBlock::RootFolders.into());
app.push_navigation_stack(ActiveRadarrBlock::DeleteRootFolderPrompt.into());
@@ -619,7 +624,7 @@ mod tests {
assert!(app.data.radarr_data.prompt_confirm);
assert_eq!(
app.data.radarr_data.prompt_confirm_action,
Some(RadarrEvent::DeleteRootFolder(None))
Some(RadarrEvent::DeleteRootFolder(1))
);
assert_eq!(
app.get_current_route(),
@@ -639,6 +644,46 @@ mod tests {
})
}
#[test]
fn test_build_add_root_folder_body() {
let mut app = App::default();
app.data.radarr_data.edit_root_folder = Some("/nfs/test".into());
let expected_add_root_folder_body = AddRootFolderBody {
path: "/nfs/test".to_owned(),
};
let actual_add_root_folder_body = RootFoldersHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveRadarrBlock::RootFolders,
None,
)
.build_add_root_folder_body();
assert_eq!(actual_add_root_folder_body, expected_add_root_folder_body);
assert!(app.data.radarr_data.edit_root_folder.is_none());
}
#[test]
fn test_extract_root_folder_id() {
let mut app = App::default();
app
.data
.radarr_data
.root_folders
.set_items(vec![root_folder()]);
let root_folder_id = RootFoldersHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveRadarrBlock::RootFolders,
None,
)
.extract_root_folder_id();
assert_eq!(root_folder_id, 1);
}
#[test]
fn test_root_folders_handler_not_ready_when_loading() {
let mut app = App::default();
@@ -2,6 +2,7 @@ use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handlers::{handle_prompt_toggle, KeyEventHandler};
use crate::models::radarr_models::RadarrTaskName;
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, SYSTEM_DETAILS_BLOCKS};
use crate::models::stateful_list::StatefulList;
use crate::models::Scrollable;
@@ -18,6 +19,18 @@ pub(super) struct SystemDetailsHandler<'a, 'b> {
_context: Option<ActiveRadarrBlock>,
}
impl<'a, 'b> SystemDetailsHandler<'a, 'b> {
fn extract_task_name(&self) -> RadarrTaskName {
self
.app
.data
.radarr_data
.tasks
.current_selection()
.task_name
}
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for SystemDetailsHandler<'a, 'b> {
fn accepts(active_block: ActiveRadarrBlock) -> bool {
SYSTEM_DETAILS_BLOCKS.contains(&active_block)
@@ -137,7 +150,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for SystemDetailsHandler
}
ActiveRadarrBlock::SystemTaskStartConfirmPrompt => {
if self.app.data.radarr_data.prompt_confirm {
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::StartTask(None));
self.app.data.radarr_data.prompt_confirm_action =
Some(RadarrEvent::StartTask(self.extract_task_name()));
}
self.app.pop_navigation_stack();
@@ -174,7 +188,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for SystemDetailsHandler
&& self.key == DEFAULT_KEYBINDINGS.confirm.key
{
self.app.data.radarr_data.prompt_confirm = true;
self.app.data.radarr_data.prompt_confirm_action = Some(RadarrEvent::StartTask(None));
self.app.data.radarr_data.prompt_confirm_action =
Some(RadarrEvent::StartTask(self.extract_task_name()));
self.app.pop_navigation_stack();
}
}
@@ -1,6 +1,6 @@
#[cfg(test)]
mod tests {
use pretty_assertions::assert_str_eq;
use pretty_assertions::{assert_eq, assert_str_eq};
use strum::IntoEnumIterator;
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
@@ -8,7 +8,7 @@ mod tests {
use crate::event::Key;
use crate::handlers::radarr_handlers::system::system_details_handler::SystemDetailsHandler;
use crate::handlers::KeyEventHandler;
use crate::models::radarr_models::RadarrTask;
use crate::models::radarr_models::{RadarrTask, RadarrTaskName};
use crate::models::servarr_data::radarr::radarr_data::{
ActiveRadarrBlock, SYSTEM_DETAILS_BLOCKS,
};
@@ -16,6 +16,7 @@ mod tests {
use crate::models::{HorizontallyScrollableText, ScrollableText};
mod test_handle_scroll_up_and_down {
use pretty_assertions::assert_eq;
use rstest::rstest;
use crate::models::{HorizontallyScrollableText, ScrollableText};
@@ -236,6 +237,7 @@ mod tests {
mod test_handle_home_end {
use crate::models::{HorizontallyScrollableText, ScrollableText};
use crate::{extended_stateful_iterable_vec, test_iterable_home_and_end};
use pretty_assertions::assert_eq;
use super::*;
@@ -676,6 +678,10 @@ mod tests {
let mut app = App::default();
app.data.radarr_data.updates = ScrollableText::with_string("Test".to_owned());
app.data.radarr_data.prompt_confirm = true;
app.data.radarr_data.tasks.set_items(vec![RadarrTask {
task_name: RadarrTaskName::default(),
..RadarrTask::default()
}]);
app.push_navigation_stack(ActiveRadarrBlock::SystemTasks.into());
app.push_navigation_stack(ActiveRadarrBlock::SystemTaskStartConfirmPrompt.into());
@@ -690,7 +696,7 @@ mod tests {
assert!(app.data.radarr_data.prompt_confirm);
assert_eq!(
app.data.radarr_data.prompt_confirm_action,
Some(RadarrEvent::StartTask(None))
Some(RadarrEvent::StartTask(RadarrTaskName::default()))
);
assert_eq!(
app.get_current_route(),
@@ -831,6 +837,7 @@ mod tests {
}
mod test_handle_key_char {
use pretty_assertions::assert_eq;
use rstest::rstest;
use crate::network::radarr_network::RadarrEvent;
@@ -894,6 +901,10 @@ mod tests {
fn test_system_tasks_start_task_prompt_confirm() {
let mut app = App::default();
app.data.radarr_data.updates = ScrollableText::with_string("Test".to_owned());
app.data.radarr_data.tasks.set_items(vec![RadarrTask {
task_name: RadarrTaskName::default(),
..RadarrTask::default()
}]);
app.push_navigation_stack(ActiveRadarrBlock::SystemTasks.into());
app.push_navigation_stack(ActiveRadarrBlock::SystemTaskStartConfirmPrompt.into());
@@ -908,7 +919,7 @@ mod tests {
assert!(app.data.radarr_data.prompt_confirm);
assert_eq!(
app.data.radarr_data.prompt_confirm_action,
Some(RadarrEvent::StartTask(None))
Some(RadarrEvent::StartTask(RadarrTaskName::default()))
);
assert_eq!(
app.get_current_route(),
@@ -928,6 +939,25 @@ mod tests {
})
}
#[test]
fn test_extract_task_name() {
let mut app = App::default();
app.data.radarr_data.tasks.set_items(vec![RadarrTask {
task_name: RadarrTaskName::default(),
..RadarrTask::default()
}]);
let task_name = SystemDetailsHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveRadarrBlock::SystemTasks,
None,
)
.extract_task_name();
assert_eq!(task_name, RadarrTaskName::default());
}
#[test]
fn test_system_details_handler_not_ready_when_loading() {
let mut app = App::default();
@@ -161,7 +161,7 @@ mod tests {
#[case(
ActiveSonarrBlock::Blocklist,
ActiveSonarrBlock::DeleteBlocklistItemPrompt,
SonarrEvent::DeleteBlocklistItem(None)
SonarrEvent::DeleteBlocklistItem(3)
)]
#[case(
ActiveSonarrBlock::Blocklist,
@@ -361,7 +361,7 @@ mod tests {
#[case(
ActiveSonarrBlock::Blocklist,
ActiveSonarrBlock::DeleteBlocklistItemPrompt,
SonarrEvent::DeleteBlocklistItem(None)
SonarrEvent::DeleteBlocklistItem(3)
)]
#[case(
ActiveSonarrBlock::Blocklist,
@@ -513,6 +513,22 @@ mod tests {
})
}
#[test]
fn test_extract_blocklist_item_id() {
let mut app = App::default();
app.data.sonarr_data.blocklist.set_items(blocklist_vec());
let blocklist_item_id = BlocklistHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::Blocklist,
None,
)
.extract_blocklist_item_id();
assert_eq!(blocklist_item_id, 3);
}
#[test]
fn test_blocklist_handler_not_ready_when_loading() {
let mut app = App::default();
+10 -4
View File
@@ -28,6 +28,10 @@ impl<'a, 'b> BlocklistHandler<'a, 'b> {
self.app.data.sonarr_data.blocklist,
BlocklistItem
);
fn extract_blocklist_item_id(&self) -> i64 {
self.app.data.sonarr_data.blocklist.current_selection().id
}
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for BlocklistHandler<'a, 'b> {
@@ -98,8 +102,9 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for BlocklistHandler<'a,
match self.active_sonarr_block {
ActiveSonarrBlock::DeleteBlocklistItemPrompt => {
if self.app.data.sonarr_data.prompt_confirm {
self.app.data.sonarr_data.prompt_confirm_action =
Some(SonarrEvent::DeleteBlocklistItem(None));
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::DeleteBlocklistItem(
self.extract_blocklist_item_id(),
));
}
self.app.pop_navigation_stack();
@@ -151,8 +156,9 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for BlocklistHandler<'a,
ActiveSonarrBlock::DeleteBlocklistItemPrompt => {
if key == DEFAULT_KEYBINDINGS.confirm.key {
self.app.data.sonarr_data.prompt_confirm = true;
self.app.data.sonarr_data.prompt_confirm_action =
Some(SonarrEvent::DeleteBlocklistItem(None));
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::DeleteBlocklistItem(
self.extract_blocklist_item_id(),
));
self.app.pop_navigation_stack();
}
@@ -1,11 +1,13 @@
#[cfg(test)]
mod tests {
use pretty_assertions::assert_eq;
use strum::IntoEnumIterator;
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handlers::sonarr_handlers::downloads::DownloadsHandler;
use crate::handlers::sonarr_handlers::sonarr_handler_test_utils::utils::download_record;
use crate::handlers::KeyEventHandler;
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, DOWNLOADS_BLOCKS};
use crate::models::sonarr_models::DownloadRecord;
@@ -138,7 +140,7 @@ mod tests {
#[case(
ActiveSonarrBlock::Downloads,
ActiveSonarrBlock::DeleteDownloadPrompt,
SonarrEvent::DeleteDownload(None)
SonarrEvent::DeleteDownload(1)
)]
#[case(
ActiveSonarrBlock::Downloads,
@@ -155,7 +157,7 @@ mod tests {
.data
.sonarr_data
.downloads
.set_items(vec![DownloadRecord::default()]);
.set_items(vec![download_record()]);
app.data.sonarr_data.prompt_confirm = true;
app.push_navigation_stack(base_route.into());
app.push_navigation_stack(prompt_block.into());
@@ -338,7 +340,7 @@ mod tests {
#[case(
ActiveSonarrBlock::Downloads,
ActiveSonarrBlock::DeleteDownloadPrompt,
SonarrEvent::DeleteDownload(None)
SonarrEvent::DeleteDownload(1)
)]
#[case(
ActiveSonarrBlock::Downloads,
@@ -355,7 +357,7 @@ mod tests {
.data
.sonarr_data
.downloads
.set_items(vec![DownloadRecord::default()]);
.set_items(vec![download_record()]);
app.push_navigation_stack(base_route.into());
app.push_navigation_stack(prompt_block.into());
@@ -387,6 +389,26 @@ mod tests {
})
}
#[test]
fn test_extract_download_id() {
let mut app = App::default();
app
.data
.sonarr_data
.downloads
.set_items(vec![download_record()]);
let download_id = DownloadsHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::Downloads,
None,
)
.extract_download_id();
assert_eq!(download_id, 1);
}
#[test]
fn test_downloads_handler_not_ready_when_loading() {
let mut app = App::default();
@@ -27,6 +27,10 @@ impl<'a, 'b> DownloadsHandler<'a, 'b> {
self.app.data.sonarr_data.downloads,
DownloadRecord
);
fn extract_download_id(&self) -> i64 {
self.app.data.sonarr_data.downloads.current_selection().id
}
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for DownloadsHandler<'a, 'b> {
@@ -95,7 +99,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for DownloadsHandler<'a,
match self.active_sonarr_block {
ActiveSonarrBlock::DeleteDownloadPrompt => {
if self.app.data.sonarr_data.prompt_confirm {
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::DeleteDownload(None));
self.app.data.sonarr_data.prompt_confirm_action =
Some(SonarrEvent::DeleteDownload(self.extract_download_id()));
}
self.app.pop_navigation_stack();
@@ -138,7 +143,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for DownloadsHandler<'a,
ActiveSonarrBlock::DeleteDownloadPrompt => {
if key == DEFAULT_KEYBINDINGS.confirm.key {
self.app.data.sonarr_data.prompt_confirm = true;
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::DeleteDownload(None));
self.app.data.sonarr_data.prompt_confirm_action =
Some(SonarrEvent::DeleteDownload(self.extract_download_id()));
self.app.pop_navigation_stack();
}
@@ -2,7 +2,9 @@ use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handlers::{handle_prompt_toggle, KeyEventHandler};
use crate::models::servarr_data::modals::EditIndexerModal;
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, EDIT_INDEXER_BLOCKS};
use crate::models::servarr_models::EditIndexerParams;
use crate::network::sonarr_network::SonarrEvent;
use crate::{handle_prompt_left_right_keys, handle_text_box_keys, handle_text_box_left_right_keys};
@@ -17,6 +19,60 @@ pub(super) struct EditIndexerHandler<'a, 'b> {
_context: Option<ActiveSonarrBlock>,
}
impl<'a, 'b> EditIndexerHandler<'a, 'b> {
fn build_edit_indexer_params(&mut self) -> EditIndexerParams {
let indexer_id = self.app.data.sonarr_data.indexers.current_selection().id;
let tags = self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_ref()
.unwrap()
.tags
.text
.clone();
let params = {
let EditIndexerModal {
name,
enable_rss,
enable_automatic_search,
enable_interactive_search,
url,
api_key,
seed_ratio,
priority,
..
} = self
.app
.data
.sonarr_data
.edit_indexer_modal
.as_ref()
.unwrap();
EditIndexerParams {
indexer_id,
name: Some(name.text.clone()),
enable_rss: Some(enable_rss.unwrap_or_default()),
enable_automatic_search: Some(enable_automatic_search.unwrap_or_default()),
enable_interactive_search: Some(enable_interactive_search.unwrap_or_default()),
url: Some(url.text.clone()),
api_key: Some(api_key.text.clone()),
seed_ratio: Some(seed_ratio.text.clone()),
tags: None,
tag_input_string: Some(tags),
priority: Some(*priority),
clear_tags: false,
}
};
self.app.data.sonarr_data.edit_indexer_modal = None;
params
}
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for EditIndexerHandler<'a, 'b> {
fn accepts(active_block: ActiveSonarrBlock) -> bool {
EDIT_INDEXER_BLOCKS.contains(&active_block)
@@ -297,12 +353,12 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for EditIndexerHandler<'
let selected_block = self.app.data.sonarr_data.selected_block.get_active_block();
match selected_block {
ActiveSonarrBlock::EditIndexerConfirmPrompt => {
let sonarr_data = &mut self.app.data.sonarr_data;
if sonarr_data.prompt_confirm {
sonarr_data.prompt_confirm_action = Some(SonarrEvent::EditIndexer(None));
if self.app.data.sonarr_data.prompt_confirm {
self.app.data.sonarr_data.prompt_confirm_action =
Some(SonarrEvent::EditIndexer(self.build_edit_indexer_params()));
self.app.should_refresh = true;
} else {
sonarr_data.edit_indexer_modal = None;
self.app.data.sonarr_data.edit_indexer_modal = None;
}
self.app.pop_navigation_stack();
@@ -464,7 +520,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for EditIndexerHandler<'
&& self.key == DEFAULT_KEYBINDINGS.confirm.key
{
self.app.data.sonarr_data.prompt_confirm = true;
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::EditIndexer(None));
self.app.data.sonarr_data.prompt_confirm_action =
Some(SonarrEvent::EditIndexer(self.build_edit_indexer_params()));
self.app.should_refresh = true;
self.app.pop_navigation_stack();
@@ -4,9 +4,12 @@ mod tests {
use crate::app::App;
use crate::event::Key;
use crate::handlers::sonarr_handlers::indexers::edit_indexer_handler::EditIndexerHandler;
use crate::handlers::sonarr_handlers::sonarr_handler_test_utils::utils::indexer;
use crate::handlers::KeyEventHandler;
use crate::models::servarr_data::modals::EditIndexerModal;
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, EDIT_INDEXER_BLOCKS};
use crate::models::servarr_models::EditIndexerParams;
use pretty_assertions::assert_eq;
use strum::IntoEnumIterator;
mod test_handle_scroll_up_and_down {
@@ -896,7 +899,32 @@ mod tests {
.sonarr_data
.selected_block
.set_index(0, EDIT_INDEXER_TORRENT_SELECTION_BLOCKS.len() - 1);
app.data.sonarr_data.edit_indexer_modal = Some(EditIndexerModal::default());
let edit_indexer_modal = EditIndexerModal {
name: "Test Update".into(),
enable_rss: Some(false),
enable_automatic_search: Some(false),
enable_interactive_search: Some(false),
url: "https://localhost:9696/1/".into(),
api_key: "test1234".into(),
seed_ratio: "1.3".into(),
tags: "usenet, testing".into(),
priority: 0,
};
app.data.sonarr_data.edit_indexer_modal = Some(edit_indexer_modal);
app.data.sonarr_data.indexers.set_items(vec![indexer()]);
let expected_edit_indexer_params = EditIndexerParams {
indexer_id: 1,
name: Some("Test Update".to_owned()),
enable_rss: Some(false),
enable_automatic_search: Some(false),
enable_interactive_search: Some(false),
url: Some("https://localhost:9696/1/".to_owned()),
api_key: Some("test1234".to_owned()),
seed_ratio: Some("1.3".to_owned()),
tag_input_string: Some("usenet, testing".to_owned()),
priority: Some(0),
..EditIndexerParams::default()
};
app.data.sonarr_data.prompt_confirm = true;
EditIndexerHandler::with(
@@ -908,11 +936,11 @@ mod tests {
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Indexers.into());
assert!(app.data.sonarr_data.edit_indexer_modal.is_some());
assert!(app.data.sonarr_data.edit_indexer_modal.is_none());
assert!(app.should_refresh);
assert_eq!(
app.data.sonarr_data.prompt_confirm_action,
Some(SonarrEvent::EditIndexer(None))
Some(SonarrEvent::EditIndexer(expected_edit_indexer_params))
);
}
@@ -1408,7 +1436,7 @@ mod tests {
use crate::models::servarr_data::sonarr::sonarr_data::EDIT_INDEXER_TORRENT_SELECTION_BLOCKS;
use crate::models::BlockSelectionState;
use crate::network::sonarr_network::SonarrEvent;
use pretty_assertions::assert_str_eq;
use pretty_assertions::{assert_eq, assert_str_eq};
use super::*;
@@ -1709,7 +1737,32 @@ mod tests {
.sonarr_data
.selected_block
.set_index(0, EDIT_INDEXER_TORRENT_SELECTION_BLOCKS.len() - 1);
app.data.sonarr_data.edit_indexer_modal = Some(EditIndexerModal::default());
let edit_indexer_modal = EditIndexerModal {
name: "Test Update".into(),
enable_rss: Some(false),
enable_automatic_search: Some(false),
enable_interactive_search: Some(false),
url: "https://localhost:9696/1/".into(),
api_key: "test1234".into(),
seed_ratio: "1.3".into(),
tags: "usenet, testing".into(),
priority: 0,
};
app.data.sonarr_data.edit_indexer_modal = Some(edit_indexer_modal);
app.data.sonarr_data.indexers.set_items(vec![indexer()]);
let expected_edit_indexer_params = EditIndexerParams {
indexer_id: 1,
name: Some("Test Update".to_owned()),
enable_rss: Some(false),
enable_automatic_search: Some(false),
enable_interactive_search: Some(false),
url: Some("https://localhost:9696/1/".to_owned()),
api_key: Some("test1234".to_owned()),
seed_ratio: Some("1.3".to_owned()),
tag_input_string: Some("usenet, testing".to_owned()),
priority: Some(0),
..EditIndexerParams::default()
};
EditIndexerHandler::with(
DEFAULT_KEYBINDINGS.confirm.key,
@@ -1720,11 +1773,11 @@ mod tests {
.handle();
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Indexers.into());
assert!(app.data.sonarr_data.edit_indexer_modal.is_some());
assert!(app.data.sonarr_data.edit_indexer_modal.is_none());
assert!(app.should_refresh);
assert_eq!(
app.data.sonarr_data.prompt_confirm_action,
Some(SonarrEvent::EditIndexer(None))
Some(SonarrEvent::EditIndexer(expected_edit_indexer_params))
);
}
}
@@ -1740,6 +1793,48 @@ mod tests {
})
}
#[test]
fn test_build_edit_indexer_params() {
let mut app = App::default();
let edit_indexer_modal = EditIndexerModal {
name: "Test Update".into(),
enable_rss: Some(false),
enable_automatic_search: Some(false),
enable_interactive_search: Some(false),
url: "https://localhost:9696/1/".into(),
api_key: "test1234".into(),
seed_ratio: "1.3".into(),
tags: "usenet, testing".into(),
priority: 0,
};
app.data.sonarr_data.edit_indexer_modal = Some(edit_indexer_modal);
app.data.sonarr_data.indexers.set_items(vec![indexer()]);
let expected_edit_indexer_params = EditIndexerParams {
indexer_id: 1,
name: Some("Test Update".to_owned()),
enable_rss: Some(false),
enable_automatic_search: Some(false),
enable_interactive_search: Some(false),
url: Some("https://localhost:9696/1/".to_owned()),
api_key: Some("test1234".to_owned()),
seed_ratio: Some("1.3".to_owned()),
tag_input_string: Some("usenet, testing".to_owned()),
priority: Some(0),
..EditIndexerParams::default()
};
let params = EditIndexerHandler::with(
DEFAULT_KEYBINDINGS.confirm.key,
&mut app,
ActiveSonarrBlock::EditIndexerPrompt,
None,
)
.build_edit_indexer_params();
assert_eq!(params, expected_edit_indexer_params);
assert!(app.data.sonarr_data.edit_indexer_modal.is_none());
}
#[test]
fn test_edit_indexer_handler_is_not_ready_when_loading() {
let mut app = App::default();
@@ -6,6 +6,7 @@ use crate::handlers::{handle_prompt_toggle, KeyEventHandler};
use crate::models::servarr_data::sonarr::sonarr_data::{
ActiveSonarrBlock, INDEXER_SETTINGS_BLOCKS,
};
use crate::models::sonarr_models::IndexerSettings;
use crate::network::sonarr_network::SonarrEvent;
#[cfg(test)]
@@ -19,6 +20,23 @@ pub(super) struct IndexerSettingsHandler<'a, 'b> {
_context: Option<ActiveSonarrBlock>,
}
impl<'a, 'b> IndexerSettingsHandler<'a, 'b> {
fn build_edit_indexer_settings_params(&mut self) -> IndexerSettings {
let indexer_settings = self
.app
.data
.sonarr_data
.indexer_settings
.as_ref()
.unwrap()
.clone();
self.app.data.sonarr_data.indexer_settings = None;
indexer_settings
}
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for IndexerSettingsHandler<'a, 'b> {
fn accepts(active_block: ActiveSonarrBlock) -> bool {
INDEXER_SETTINGS_BLOCKS.contains(&active_block)
@@ -119,12 +137,13 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for IndexerSettingsHandl
ActiveSonarrBlock::AllIndexerSettingsPrompt => {
match self.app.data.sonarr_data.selected_block.get_active_block() {
ActiveSonarrBlock::IndexerSettingsConfirmPrompt => {
let sonarr_data = &mut self.app.data.sonarr_data;
if sonarr_data.prompt_confirm {
sonarr_data.prompt_confirm_action = Some(SonarrEvent::EditAllIndexerSettings(None));
if self.app.data.sonarr_data.prompt_confirm {
self.app.data.sonarr_data.prompt_confirm_action = Some(
SonarrEvent::EditAllIndexerSettings(self.build_edit_indexer_settings_params()),
);
self.app.should_refresh = true;
} else {
sonarr_data.indexer_settings = None;
self.app.data.sonarr_data.indexer_settings = None;
}
self.app.pop_navigation_stack();
@@ -172,8 +191,9 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for IndexerSettingsHandl
&& self.key == DEFAULT_KEYBINDINGS.confirm.key
{
self.app.data.sonarr_data.prompt_confirm = true;
self.app.data.sonarr_data.prompt_confirm_action =
Some(SonarrEvent::EditAllIndexerSettings(None));
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::EditAllIndexerSettings(
self.build_edit_indexer_settings_params(),
));
self.app.should_refresh = true;
self.app.pop_navigation_stack();
@@ -1,11 +1,13 @@
#[cfg(test)]
mod tests {
use pretty_assertions::assert_eq;
use strum::IntoEnumIterator;
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handlers::sonarr_handlers::indexers::edit_indexer_settings_handler::IndexerSettingsHandler;
use crate::handlers::sonarr_handlers::sonarr_handler_test_utils::utils::indexer_settings;
use crate::handlers::KeyEventHandler;
use crate::models::servarr_data::sonarr::sonarr_data::{
ActiveSonarrBlock, INDEXER_SETTINGS_BLOCKS,
@@ -285,7 +287,7 @@ mod tests {
.sonarr_data
.selected_block
.set_index(0, INDEXER_SETTINGS_SELECTION_BLOCKS.len() - 1);
app.data.sonarr_data.indexer_settings = Some(IndexerSettings::default());
app.data.sonarr_data.indexer_settings = Some(indexer_settings());
app.data.sonarr_data.prompt_confirm = true;
IndexerSettingsHandler::with(
@@ -299,9 +301,9 @@ mod tests {
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Indexers.into());
assert_eq!(
app.data.sonarr_data.prompt_confirm_action,
Some(SonarrEvent::EditAllIndexerSettings(None))
Some(SonarrEvent::EditAllIndexerSettings(indexer_settings()))
);
assert!(app.data.sonarr_data.indexer_settings.is_some());
assert!(app.data.sonarr_data.indexer_settings.is_none());
assert!(app.should_refresh);
}
@@ -468,11 +470,11 @@ mod tests {
mod test_handle_key_char {
use crate::{
models::{
servarr_data::sonarr::sonarr_data::INDEXER_SETTINGS_SELECTION_BLOCKS,
sonarr_models::IndexerSettings, BlockSelectionState,
servarr_data::sonarr::sonarr_data::INDEXER_SETTINGS_SELECTION_BLOCKS, BlockSelectionState,
},
network::sonarr_network::SonarrEvent,
};
use pretty_assertions::assert_eq;
use super::*;
@@ -488,7 +490,7 @@ mod tests {
.sonarr_data
.selected_block
.set_index(0, INDEXER_SETTINGS_SELECTION_BLOCKS.len() - 1);
app.data.sonarr_data.indexer_settings = Some(IndexerSettings::default());
app.data.sonarr_data.indexer_settings = Some(indexer_settings());
IndexerSettingsHandler::with(
DEFAULT_KEYBINDINGS.confirm.key,
@@ -501,9 +503,9 @@ mod tests {
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Indexers.into());
assert_eq!(
app.data.sonarr_data.prompt_confirm_action,
Some(SonarrEvent::EditAllIndexerSettings(None))
Some(SonarrEvent::EditAllIndexerSettings(indexer_settings()))
);
assert!(app.data.sonarr_data.indexer_settings.is_some());
assert!(app.data.sonarr_data.indexer_settings.is_none());
assert!(app.should_refresh);
}
}
@@ -519,6 +521,23 @@ mod tests {
})
}
#[test]
fn test_build_edit_indexer_settings_params() {
let mut app = App::default();
app.data.sonarr_data.indexer_settings = Some(indexer_settings());
let actual_indexer_settings = IndexerSettingsHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::AllIndexerSettingsPrompt,
None,
)
.build_edit_indexer_settings_params();
assert_eq!(actual_indexer_settings, indexer_settings());
assert!(app.data.sonarr_data.indexer_settings.is_none());
}
#[test]
fn test_edit_indexer_settings_handler_not_ready_when_loading() {
let mut app = App::default();
@@ -1,5 +1,6 @@
#[cfg(test)]
mod tests {
use pretty_assertions::assert_eq;
use rstest::rstest;
use strum::IntoEnumIterator;
@@ -7,6 +8,7 @@ mod tests {
use crate::app::App;
use crate::event::Key;
use crate::handlers::sonarr_handlers::indexers::IndexersHandler;
use crate::handlers::sonarr_handlers::sonarr_handler_test_utils::utils::indexer;
use crate::handlers::KeyEventHandler;
use crate::models::servarr_data::sonarr::sonarr_data::{
ActiveSonarrBlock, EDIT_INDEXER_BLOCKS, INDEXERS_BLOCKS, INDEXER_SETTINGS_BLOCKS,
@@ -245,11 +247,7 @@ mod tests {
#[test]
fn test_delete_indexer_prompt_confirm_submit() {
let mut app = App::default();
app
.data
.sonarr_data
.indexers
.set_items(vec![Indexer::default()]);
app.data.sonarr_data.indexers.set_items(vec![indexer()]);
app.data.sonarr_data.prompt_confirm = true;
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.push_navigation_stack(ActiveSonarrBlock::DeleteIndexerPrompt.into());
@@ -265,7 +263,7 @@ mod tests {
assert!(app.data.sonarr_data.prompt_confirm);
assert_eq!(
app.data.sonarr_data.prompt_confirm_action,
Some(SonarrEvent::DeleteIndexer(None))
Some(SonarrEvent::DeleteIndexer(1))
);
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Indexers.into());
}
@@ -556,11 +554,7 @@ mod tests {
#[test]
fn test_delete_indexer_prompt_confirm() {
let mut app = App::default();
app
.data
.sonarr_data
.indexers
.set_items(vec![Indexer::default()]);
app.data.sonarr_data.indexers.set_items(vec![indexer()]);
app.push_navigation_stack(ActiveSonarrBlock::Indexers.into());
app.push_navigation_stack(ActiveSonarrBlock::DeleteIndexerPrompt.into());
@@ -575,7 +569,7 @@ mod tests {
assert!(app.data.sonarr_data.prompt_confirm);
assert_eq!(
app.data.sonarr_data.prompt_confirm_action,
Some(SonarrEvent::DeleteIndexer(None))
Some(SonarrEvent::DeleteIndexer(1))
);
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Indexers.into());
}
@@ -649,6 +643,22 @@ mod tests {
})
}
#[test]
fn test_extract_indexer_id() {
let mut app = App::default();
app.data.sonarr_data.indexers.set_items(vec![indexer()]);
let indexer_id = IndexersHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::Indexers,
None,
)
.extract_indexer_id();
assert_eq!(indexer_id, 1);
}
#[test]
fn test_indexers_handler_not_ready_when_loading() {
let mut app = App::default();
+9 -4
View File
@@ -33,6 +33,10 @@ pub(super) struct IndexersHandler<'a, 'b> {
impl<'a, 'b> IndexersHandler<'a, 'b> {
handle_table_events!(self, indexers, self.app.data.sonarr_data.indexers, Indexer);
fn extract_indexer_id(&self) -> i64 {
self.app.data.sonarr_data.indexers.current_selection().id
}
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for IndexersHandler<'a, 'b> {
@@ -115,9 +119,9 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for IndexersHandler<'a,
fn handle_submit(&mut self) {
match self.active_sonarr_block {
ActiveSonarrBlock::DeleteIndexerPrompt => {
let sonarr_data = &mut self.app.data.sonarr_data;
if sonarr_data.prompt_confirm {
sonarr_data.prompt_confirm_action = Some(SonarrEvent::DeleteIndexer(None));
if self.app.data.sonarr_data.prompt_confirm {
self.app.data.sonarr_data.prompt_confirm_action =
Some(SonarrEvent::DeleteIndexer(self.extract_indexer_id()));
}
self.app.pop_navigation_stack();
@@ -189,7 +193,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for IndexersHandler<'a,
ActiveSonarrBlock::DeleteIndexerPrompt => {
if key == DEFAULT_KEYBINDINGS.confirm.key {
self.app.data.sonarr_data.prompt_confirm = true;
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::DeleteIndexer(None));
self.app.data.sonarr_data.prompt_confirm_action =
Some(SonarrEvent::DeleteIndexer(self.extract_indexer_id()));
self.app.pop_navigation_stack();
}
@@ -1,10 +1,12 @@
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::handlers::table_handler::TableHandlingConfig;
use crate::handlers::{handle_prompt_toggle, KeyEventHandler};
use crate::models::servarr_data::sonarr::modals::AddSeriesModal;
use crate::models::servarr_data::sonarr::sonarr_data::{
ActiveSonarrBlock, ADD_SERIES_BLOCKS, ADD_SERIES_SELECTION_BLOCKS,
};
use crate::models::sonarr_models::AddSeriesSearchResult;
use crate::models::sonarr_models::{AddSeriesBody, AddSeriesOptions, AddSeriesSearchResult};
use crate::models::stateful_table::StatefulTable;
use crate::models::{BlockSelectionState, Scrollable};
use crate::network::sonarr_network::SonarrEvent;
use crate::{handle_table_events, handle_text_box_keys, handle_text_box_left_right_keys, App, Key};
@@ -30,9 +32,90 @@ impl<'a, 'b> AddSeriesHandler<'a, 'b> {
.sonarr_data
.add_searched_series
.as_mut()
.unwrap(),
.unwrap_or(&mut StatefulTable::default()),
AddSeriesSearchResult
);
fn build_add_series_body(&mut self) -> AddSeriesBody {
let tags = self
.app
.data
.sonarr_data
.add_series_modal
.as_ref()
.unwrap()
.tags
.text
.clone();
let AddSeriesModal {
root_folder_list,
monitor_list,
quality_profile_list,
language_profile_list,
series_type_list,
use_season_folder,
..
} = self.app.data.sonarr_data.add_series_modal.as_ref().unwrap();
let season_folder = *use_season_folder;
let (tvdb_id, title) = {
let AddSeriesSearchResult { tvdb_id, title, .. } = self
.app
.data
.sonarr_data
.add_searched_series
.as_ref()
.unwrap()
.current_selection()
.clone();
(tvdb_id, title.text)
};
let quality_profile = quality_profile_list.current_selection();
let quality_profile_id = *self
.app
.data
.sonarr_data
.quality_profile_map
.iter()
.filter(|(_, value)| *value == quality_profile)
.map(|(key, _)| key)
.next()
.unwrap();
let language_profile = language_profile_list.current_selection();
let language_profile_id = *self
.app
.data
.sonarr_data
.language_profiles_map
.iter()
.filter(|(_, value)| *value == language_profile)
.map(|(key, _)| key)
.next()
.unwrap();
let path = root_folder_list.current_selection().path.clone();
let monitor = monitor_list.current_selection().to_string();
let series_type = series_type_list.current_selection().to_string();
self.app.data.sonarr_data.add_series_modal = None;
AddSeriesBody {
tvdb_id,
title,
monitored: true,
root_folder_path: path,
quality_profile_id,
language_profile_id,
series_type,
season_folder,
tags: Vec::new(),
tag_input_string: Some(tags),
add_options: AddSeriesOptions {
monitor,
search_for_cutoff_unmet_episodes: true,
search_for_missing_episodes: true,
},
}
}
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for AddSeriesHandler<'a, 'b> {
@@ -403,7 +486,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for AddSeriesHandler<'a,
match self.app.data.sonarr_data.selected_block.get_active_block() {
ActiveSonarrBlock::AddSeriesConfirmPrompt => {
if self.app.data.sonarr_data.prompt_confirm {
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::AddSeries(None));
self.app.data.sonarr_data.prompt_confirm_action =
Some(SonarrEvent::AddSeries(self.build_add_series_body()));
}
self.app.pop_navigation_stack();
@@ -534,7 +618,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for AddSeriesHandler<'a,
&& key == DEFAULT_KEYBINDINGS.confirm.key
{
self.app.data.sonarr_data.prompt_confirm = true;
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::AddSeries(None));
self.app.data.sonarr_data.prompt_confirm_action =
Some(SonarrEvent::AddSeries(self.build_add_series_body()));
self.app.pop_navigation_stack();
}
}
@@ -1,16 +1,22 @@
#[cfg(test)]
mod tests {
use pretty_assertions::assert_str_eq;
use bimap::BiMap;
use pretty_assertions::{assert_eq, assert_str_eq};
use strum::IntoEnumIterator;
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handlers::sonarr_handlers::library::add_series_handler::AddSeriesHandler;
use crate::handlers::sonarr_handlers::sonarr_handler_test_utils::utils::add_series_search_result;
use crate::handlers::KeyEventHandler;
use crate::models::servarr_data::sonarr::modals::AddSeriesModal;
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, ADD_SERIES_BLOCKS};
use crate::models::servarr_models::RootFolder;
use crate::models::sonarr_models::{AddSeriesSearchResult, SeriesMonitor, SeriesType};
use crate::models::sonarr_models::{
AddSeriesBody, AddSeriesOptions, AddSeriesSearchResult, SeriesMonitor, SeriesType,
};
use crate::models::stateful_table::StatefulTable;
use crate::models::HorizontallyScrollableText;
mod test_handle_scroll_up_and_down {
@@ -366,6 +372,7 @@ mod tests {
}
mod test_handle_home_end {
use pretty_assertions::assert_eq;
use std::sync::atomic::Ordering;
use strum::IntoEnumIterator;
@@ -763,6 +770,7 @@ mod tests {
}
mod test_handle_left_right_action {
use pretty_assertions::assert_eq;
use std::sync::atomic::Ordering;
use crate::models::servarr_data::sonarr::modals::AddSeriesModal;
@@ -1109,10 +1117,67 @@ mod tests {
#[test]
fn test_add_series_confirm_prompt_prompt_confirmation_submit() {
let mut app = App::default();
app.data.sonarr_data.add_series_modal = Some(AddSeriesModal::default());
app.push_navigation_stack(ActiveSonarrBlock::Series.into());
app.push_navigation_stack(ActiveSonarrBlock::AddSeriesPrompt.into());
app.data.sonarr_data.prompt_confirm = true;
let mut add_series_modal = AddSeriesModal {
use_season_folder: true,
tags: "usenet, testing".into(),
..AddSeriesModal::default()
};
add_series_modal.root_folder_list.set_items(vec![
RootFolder {
id: 1,
path: "/nfs".to_owned(),
accessible: true,
free_space: 219902325555200,
unmapped_folders: None,
},
RootFolder {
id: 2,
path: "/nfs2".to_owned(),
accessible: true,
free_space: 21990232555520,
unmapped_folders: None,
},
]);
add_series_modal.root_folder_list.state.select(Some(1));
add_series_modal
.quality_profile_list
.set_items(vec!["HD - 1080p".to_owned()]);
add_series_modal
.language_profile_list
.set_items(vec!["English".to_owned()]);
add_series_modal
.monitor_list
.set_items(Vec::from_iter(SeriesMonitor::iter()));
add_series_modal
.series_type_list
.set_items(Vec::from_iter(SeriesType::iter()));
app.data.sonarr_data.add_series_modal = Some(add_series_modal);
app.data.sonarr_data.quality_profile_map =
BiMap::from_iter([(2222, "HD - 1080p".to_owned())]);
app.data.sonarr_data.language_profiles_map = BiMap::from_iter([(2222, "English".to_owned())]);
let mut add_searched_series = StatefulTable::default();
add_searched_series.set_items(vec![add_series_search_result()]);
app.data.sonarr_data.add_searched_series = Some(add_searched_series);
let expected_add_series_body = AddSeriesBody {
tvdb_id: 1234,
title: "Test".to_owned(),
monitored: true,
root_folder_path: "/nfs2".to_owned(),
quality_profile_id: 2222,
language_profile_id: 2222,
series_type: "standard".to_owned(),
season_folder: true,
tags: Vec::default(),
tag_input_string: Some("usenet, testing".to_owned()),
add_options: AddSeriesOptions {
monitor: "all".to_owned(),
search_for_cutoff_unmet_episodes: true,
search_for_missing_episodes: true,
},
};
app.data.sonarr_data.selected_block = BlockSelectionState::new(ADD_SERIES_SELECTION_BLOCKS);
app
.data
@@ -1131,9 +1196,9 @@ mod tests {
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Series.into());
assert_eq!(
app.data.sonarr_data.prompt_confirm_action,
Some(SonarrEvent::AddSeries(None))
Some(SonarrEvent::AddSeries(expected_add_series_body))
);
assert!(app.data.sonarr_data.add_series_modal.is_some());
assert!(app.data.sonarr_data.add_series_modal.is_none());
}
#[rstest]
@@ -1440,6 +1505,7 @@ mod tests {
},
network::sonarr_network::SonarrEvent,
};
use pretty_assertions::assert_eq;
#[test]
fn test_add_series_search_input_backspace() {
@@ -1553,7 +1619,64 @@ mod tests {
#[test]
fn test_add_series_confirm_prompt_prompt_confirmation_confirm() {
let mut app = App::default();
app.data.sonarr_data.add_series_modal = Some(AddSeriesModal::default());
let mut add_series_modal = AddSeriesModal {
use_season_folder: true,
tags: "usenet, testing".into(),
..AddSeriesModal::default()
};
add_series_modal.root_folder_list.set_items(vec![
RootFolder {
id: 1,
path: "/nfs".to_owned(),
accessible: true,
free_space: 219902325555200,
unmapped_folders: None,
},
RootFolder {
id: 2,
path: "/nfs2".to_owned(),
accessible: true,
free_space: 21990232555520,
unmapped_folders: None,
},
]);
add_series_modal.root_folder_list.state.select(Some(1));
add_series_modal
.quality_profile_list
.set_items(vec!["HD - 1080p".to_owned()]);
add_series_modal
.language_profile_list
.set_items(vec!["English".to_owned()]);
add_series_modal
.monitor_list
.set_items(Vec::from_iter(SeriesMonitor::iter()));
add_series_modal
.series_type_list
.set_items(Vec::from_iter(SeriesType::iter()));
app.data.sonarr_data.add_series_modal = Some(add_series_modal);
app.data.sonarr_data.quality_profile_map =
BiMap::from_iter([(2222, "HD - 1080p".to_owned())]);
app.data.sonarr_data.language_profiles_map = BiMap::from_iter([(2222, "English".to_owned())]);
let mut add_searched_series = StatefulTable::default();
add_searched_series.set_items(vec![add_series_search_result()]);
app.data.sonarr_data.add_searched_series = Some(add_searched_series);
let expected_add_series_body = AddSeriesBody {
tvdb_id: 1234,
title: "Test".to_owned(),
monitored: true,
root_folder_path: "/nfs2".to_owned(),
quality_profile_id: 2222,
language_profile_id: 2222,
series_type: "standard".to_owned(),
season_folder: true,
tags: Vec::default(),
tag_input_string: Some("usenet, testing".to_owned()),
add_options: AddSeriesOptions {
monitor: "all".to_owned(),
search_for_cutoff_unmet_episodes: true,
search_for_missing_episodes: true,
},
};
app.push_navigation_stack(ActiveSonarrBlock::Series.into());
app.push_navigation_stack(ActiveSonarrBlock::AddSeriesPrompt.into());
app.data.sonarr_data.selected_block = BlockSelectionState::new(ADD_SERIES_SELECTION_BLOCKS);
@@ -1574,9 +1697,9 @@ mod tests {
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Series.into());
assert_eq!(
app.data.sonarr_data.prompt_confirm_action,
Some(SonarrEvent::AddSeries(None))
Some(SonarrEvent::AddSeries(expected_add_series_body))
);
assert!(app.data.sonarr_data.add_series_modal.is_some());
assert!(app.data.sonarr_data.add_series_modal.is_none());
}
}
@@ -1591,6 +1714,93 @@ mod tests {
});
}
#[test]
fn test_add_series_search_no_panic_on_none_search_result() {
let mut app = App::default();
app.data.sonarr_data.add_series_search = None;
AddSeriesHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::AddSeriesSearchResults,
None,
)
.handle();
}
#[test]
fn test_build_add_series_body() {
let mut app = App::default();
let mut add_series_modal = AddSeriesModal {
use_season_folder: true,
tags: "usenet, testing".into(),
..AddSeriesModal::default()
};
add_series_modal.root_folder_list.set_items(vec![
RootFolder {
id: 1,
path: "/nfs".to_owned(),
accessible: true,
free_space: 219902325555200,
unmapped_folders: None,
},
RootFolder {
id: 2,
path: "/nfs2".to_owned(),
accessible: true,
free_space: 21990232555520,
unmapped_folders: None,
},
]);
add_series_modal.root_folder_list.state.select(Some(1));
add_series_modal
.quality_profile_list
.set_items(vec!["HD - 1080p".to_owned()]);
add_series_modal
.language_profile_list
.set_items(vec!["English".to_owned()]);
add_series_modal
.monitor_list
.set_items(Vec::from_iter(SeriesMonitor::iter()));
add_series_modal
.series_type_list
.set_items(Vec::from_iter(SeriesType::iter()));
app.data.sonarr_data.add_series_modal = Some(add_series_modal);
app.data.sonarr_data.quality_profile_map = BiMap::from_iter([(2222, "HD - 1080p".to_owned())]);
app.data.sonarr_data.language_profiles_map = BiMap::from_iter([(2222, "English".to_owned())]);
let mut add_searched_series = StatefulTable::default();
add_searched_series.set_items(vec![add_series_search_result()]);
app.data.sonarr_data.add_searched_series = Some(add_searched_series);
let expected_add_series_body = AddSeriesBody {
tvdb_id: 1234,
title: "Test".to_owned(),
monitored: true,
root_folder_path: "/nfs2".to_owned(),
quality_profile_id: 2222,
language_profile_id: 2222,
series_type: "standard".to_owned(),
season_folder: true,
tags: Vec::default(),
tag_input_string: Some("usenet, testing".to_owned()),
add_options: AddSeriesOptions {
monitor: "all".to_owned(),
search_for_cutoff_unmet_episodes: true,
search_for_missing_episodes: true,
},
};
let add_series_body = AddSeriesHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::AddSeriesPrompt,
None,
)
.build_add_series_body();
assert_eq!(add_series_body, expected_add_series_body);
assert!(app.data.sonarr_data.add_series_modal.is_none());
}
#[test]
fn test_add_series_handler_is_not_ready_when_loading() {
let mut app = App::default();
@@ -1,9 +1,10 @@
use crate::models::sonarr_models::DeleteSeriesParams;
use crate::network::sonarr_network::SonarrEvent;
use crate::{
app::{key_binding::DEFAULT_KEYBINDINGS, App},
event::Key,
handlers::{handle_prompt_toggle, KeyEventHandler},
models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, DELETE_SERIES_BLOCKS},
network::sonarr_network::SonarrEvent,
};
#[cfg(test)]
@@ -17,6 +18,21 @@ pub(super) struct DeleteSeriesHandler<'a, 'b> {
_context: Option<ActiveSonarrBlock>,
}
impl<'a, 'b> DeleteSeriesHandler<'a, 'b> {
fn build_delete_series_params(&mut self) -> DeleteSeriesParams {
let id = self.app.data.sonarr_data.series.current_selection().id;
let delete_series_files = self.app.data.sonarr_data.delete_series_files;
let add_list_exclusion = self.app.data.sonarr_data.add_list_exclusion;
self.app.data.sonarr_data.reset_delete_series_preferences();
DeleteSeriesParams {
id,
delete_series_files,
add_list_exclusion,
}
}
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for DeleteSeriesHandler<'a, 'b> {
fn accepts(active_block: ActiveSonarrBlock) -> bool {
DELETE_SERIES_BLOCKS.contains(&active_block)
@@ -73,7 +89,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for DeleteSeriesHandler<
match self.app.data.sonarr_data.selected_block.get_active_block() {
ActiveSonarrBlock::DeleteSeriesConfirmPrompt => {
if self.app.data.sonarr_data.prompt_confirm {
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::DeleteSeries(None));
self.app.data.sonarr_data.prompt_confirm_action =
Some(SonarrEvent::DeleteSeries(self.build_delete_series_params()));
self.app.should_refresh = true;
} else {
self.app.data.sonarr_data.reset_delete_series_preferences();
@@ -109,7 +126,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for DeleteSeriesHandler<
&& self.key == DEFAULT_KEYBINDINGS.confirm.key
{
self.app.data.sonarr_data.prompt_confirm = true;
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::DeleteSeries(None));
self.app.data.sonarr_data.prompt_confirm_action =
Some(SonarrEvent::DeleteSeries(self.build_delete_series_params()));
self.app.should_refresh = true;
self.app.pop_navigation_stack();
@@ -1,13 +1,16 @@
#[cfg(test)]
mod tests {
use pretty_assertions::assert_eq;
use strum::IntoEnumIterator;
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handlers::sonarr_handlers::library::delete_series_handler::DeleteSeriesHandler;
use crate::handlers::sonarr_handlers::sonarr_handler_test_utils::utils::series;
use crate::handlers::KeyEventHandler;
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, DELETE_SERIES_BLOCKS};
use crate::models::sonarr_models::DeleteSeriesParams;
mod test_handle_scroll_up_and_down {
use pretty_assertions::assert_eq;
@@ -132,6 +135,12 @@ mod tests {
app.data.sonarr_data.prompt_confirm = true;
app.data.sonarr_data.delete_series_files = true;
app.data.sonarr_data.add_list_exclusion = true;
app.data.sonarr_data.series.set_items(vec![series()]);
let expected_delete_series_params = DeleteSeriesParams {
id: 1,
delete_series_files: true,
add_list_exclusion: true,
};
app.data.sonarr_data.selected_block =
BlockSelectionState::new(DELETE_SERIES_SELECTION_BLOCKS);
app
@@ -151,12 +160,12 @@ mod tests {
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Series.into());
assert_eq!(
app.data.sonarr_data.prompt_confirm_action,
Some(SonarrEvent::DeleteSeries(None))
Some(SonarrEvent::DeleteSeries(expected_delete_series_params))
);
assert!(app.should_refresh);
assert!(app.data.sonarr_data.prompt_confirm);
assert!(app.data.sonarr_data.delete_series_files);
assert!(app.data.sonarr_data.add_list_exclusion);
assert!(!app.data.sonarr_data.delete_series_files);
assert!(!app.data.sonarr_data.add_list_exclusion);
}
#[test]
@@ -222,6 +231,7 @@ mod tests {
mod test_handle_esc {
use super::*;
use pretty_assertions::assert_eq;
use rstest::rstest;
const ESC_KEY: Key = DEFAULT_KEYBINDINGS.esc.key;
@@ -258,6 +268,7 @@ mod tests {
},
network::sonarr_network::SonarrEvent,
};
use pretty_assertions::assert_eq;
use super::*;
@@ -268,6 +279,12 @@ mod tests {
app.push_navigation_stack(ActiveSonarrBlock::DeleteSeriesPrompt.into());
app.data.sonarr_data.delete_series_files = true;
app.data.sonarr_data.add_list_exclusion = true;
app.data.sonarr_data.series.set_items(vec![series()]);
let expected_delete_series_params = DeleteSeriesParams {
id: 1,
delete_series_files: true,
add_list_exclusion: true,
};
app.data.sonarr_data.selected_block =
BlockSelectionState::new(DELETE_SERIES_SELECTION_BLOCKS);
app
@@ -287,12 +304,12 @@ mod tests {
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Series.into());
assert_eq!(
app.data.sonarr_data.prompt_confirm_action,
Some(SonarrEvent::DeleteSeries(None))
Some(SonarrEvent::DeleteSeries(expected_delete_series_params))
);
assert!(app.should_refresh);
assert!(app.data.sonarr_data.prompt_confirm);
assert!(app.data.sonarr_data.delete_series_files);
assert!(app.data.sonarr_data.add_list_exclusion);
assert!(!app.data.sonarr_data.delete_series_files);
assert!(!app.data.sonarr_data.add_list_exclusion);
}
}
@@ -307,6 +324,31 @@ mod tests {
});
}
#[test]
fn test_build_delete_series_params() {
let mut app = App::default();
app.data.sonarr_data.series.set_items(vec![series()]);
app.data.sonarr_data.delete_series_files = true;
app.data.sonarr_data.add_list_exclusion = true;
let expected_delete_series_params = DeleteSeriesParams {
id: 1,
delete_series_files: true,
add_list_exclusion: true,
};
let delete_series_params = DeleteSeriesHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::DeleteSeriesPrompt,
None,
)
.build_delete_series_params();
assert_eq!(delete_series_params, expected_delete_series_params);
assert!(!app.data.sonarr_data.delete_series_files);
assert!(!app.data.sonarr_data.add_list_exclusion);
}
#[test]
fn test_delete_series_handler_not_ready_when_loading() {
let mut app = App::default();
@@ -2,7 +2,9 @@ use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handlers::{handle_prompt_toggle, KeyEventHandler};
use crate::models::servarr_data::sonarr::modals::EditSeriesModal;
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, EDIT_SERIES_BLOCKS};
use crate::models::sonarr_models::EditSeriesParams;
use crate::models::Scrollable;
use crate::network::sonarr_network::SonarrEvent;
use crate::{handle_text_box_keys, handle_text_box_left_right_keys};
@@ -18,6 +20,78 @@ pub(super) struct EditSeriesHandler<'a, 'b> {
context: Option<ActiveSonarrBlock>,
}
impl<'a, 'b> EditSeriesHandler<'a, 'b> {
fn build_edit_series_params(&mut self) -> EditSeriesParams {
let series_id = self.app.data.sonarr_data.series.current_selection().id;
let tags = self
.app
.data
.sonarr_data
.edit_series_modal
.as_ref()
.unwrap()
.tags
.text
.clone();
let params = {
let EditSeriesModal {
monitored,
use_season_folders,
path,
series_type_list,
quality_profile_list,
language_profile_list,
..
} = self
.app
.data
.sonarr_data
.edit_series_modal
.as_ref()
.unwrap();
let quality_profile = quality_profile_list.current_selection();
let quality_profile_id = *self
.app
.data
.sonarr_data
.quality_profile_map
.iter()
.filter(|(_, value)| *value == quality_profile)
.map(|(key, _)| key)
.next()
.unwrap();
let language_profile = language_profile_list.current_selection();
let language_profile_id = *self
.app
.data
.sonarr_data
.language_profiles_map
.iter()
.filter(|(_, value)| *value == language_profile)
.map(|(key, _)| key)
.next()
.unwrap();
EditSeriesParams {
series_id,
monitored: Some(monitored.unwrap_or_default()),
use_season_folders: Some(use_season_folders.unwrap_or_default()),
series_type: Some(*series_type_list.current_selection()),
quality_profile_id: Some(quality_profile_id),
language_profile_id: Some(language_profile_id),
root_folder_path: Some(path.text.clone()),
tag_input_string: Some(tags),
..EditSeriesParams::default()
}
};
self.app.data.sonarr_data.edit_series_modal = None;
params
}
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for EditSeriesHandler<'a, 'b> {
fn accepts(active_block: ActiveSonarrBlock) -> bool {
EDIT_SERIES_BLOCKS.contains(&active_block)
@@ -258,7 +332,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for EditSeriesHandler<'a
match self.app.data.sonarr_data.selected_block.get_active_block() {
ActiveSonarrBlock::EditSeriesConfirmPrompt => {
if self.app.data.sonarr_data.prompt_confirm {
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::EditSeries(None));
self.app.data.sonarr_data.prompt_confirm_action =
Some(SonarrEvent::EditSeries(self.build_edit_series_params()));
self.app.should_refresh = true;
}
@@ -392,7 +467,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for EditSeriesHandler<'a
&& key == DEFAULT_KEYBINDINGS.confirm.key
{
self.app.data.sonarr_data.prompt_confirm = true;
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::EditSeries(None));
self.app.data.sonarr_data.prompt_confirm_action =
Some(SonarrEvent::EditSeries(self.build_edit_series_params()));
self.app.should_refresh = true;
self.app.pop_navigation_stack();
@@ -1,16 +1,18 @@
#[cfg(test)]
mod tests {
use pretty_assertions::assert_str_eq;
use bimap::BiMap;
use pretty_assertions::{assert_eq, assert_str_eq};
use strum::IntoEnumIterator;
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handlers::sonarr_handlers::library::edit_series_handler::EditSeriesHandler;
use crate::handlers::sonarr_handlers::sonarr_handler_test_utils::utils::series;
use crate::handlers::KeyEventHandler;
use crate::models::servarr_data::sonarr::modals::EditSeriesModal;
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, EDIT_SERIES_BLOCKS};
use crate::models::sonarr_models::SeriesType;
use crate::models::sonarr_models::{EditSeriesParams, Series, SeriesType};
mod test_handle_scroll_up_and_down {
use pretty_assertions::assert_eq;
@@ -243,6 +245,7 @@ mod tests {
}
mod test_handle_home_end {
use pretty_assertions::assert_eq;
use std::sync::atomic::Ordering;
use strum::IntoEnumIterator;
@@ -531,6 +534,7 @@ mod tests {
}
mod test_handle_left_right_action {
use pretty_assertions::assert_eq;
use std::sync::atomic::Ordering;
use crate::models::servarr_data::sonarr::modals::EditSeriesModal;
@@ -770,7 +774,43 @@ mod tests {
#[test]
fn test_edit_series_confirm_prompt_prompt_confirmation_submit() {
let mut app = App::default();
app.data.sonarr_data.edit_series_modal = Some(EditSeriesModal::default());
let mut edit_series = EditSeriesModal {
tags: "usenet, testing".to_owned().into(),
path: "/nfs/Test Path".to_owned().into(),
monitored: Some(false),
use_season_folders: Some(false),
..EditSeriesModal::default()
};
edit_series
.quality_profile_list
.set_items(vec!["Any".to_owned(), "HD - 1080p".to_owned()]);
edit_series
.language_profile_list
.set_items(vec!["Any".to_owned(), "English".to_owned()]);
edit_series
.series_type_list
.set_items(Vec::from_iter(SeriesType::iter()));
app.data.sonarr_data.edit_series_modal = Some(edit_series);
app.data.sonarr_data.series.set_items(vec![Series {
monitored: false,
season_folder: false,
..series()
}]);
app.data.sonarr_data.quality_profile_map =
BiMap::from_iter([(1111, "Any".to_owned()), (2222, "HD - 1080p".to_owned())]);
app.data.sonarr_data.language_profiles_map =
BiMap::from_iter([(1111, "Any".to_owned()), (2222, "English".to_owned())]);
let expected_edit_series_params = EditSeriesParams {
series_id: 1,
monitored: Some(false),
use_season_folders: Some(false),
series_type: Some(SeriesType::Standard),
quality_profile_id: Some(1111),
language_profile_id: Some(1111),
root_folder_path: Some("/nfs/Test Path".to_owned()),
tag_input_string: Some("usenet, testing".to_owned()),
..EditSeriesParams::default()
};
app.push_navigation_stack(ActiveSonarrBlock::Series.into());
app.push_navigation_stack(ActiveSonarrBlock::EditSeriesPrompt.into());
app.data.sonarr_data.prompt_confirm = true;
@@ -792,9 +832,9 @@ mod tests {
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Series.into());
assert_eq!(
app.data.sonarr_data.prompt_confirm_action,
Some(SonarrEvent::EditSeries(None))
Some(SonarrEvent::EditSeries(expected_edit_series_params))
);
assert!(app.data.sonarr_data.edit_series_modal.is_some());
assert!(app.data.sonarr_data.edit_series_modal.is_none());
assert!(app.should_refresh);
}
@@ -1135,6 +1175,7 @@ mod tests {
},
network::sonarr_network::SonarrEvent,
};
use pretty_assertions::assert_eq;
#[test]
fn test_edit_series_path_input_backspace() {
@@ -1253,7 +1294,43 @@ mod tests {
#[test]
fn test_edit_series_confirm_prompt_prompt_confirm() {
let mut app = App::default();
app.data.sonarr_data.edit_series_modal = Some(EditSeriesModal::default());
let mut edit_series = EditSeriesModal {
tags: "usenet, testing".to_owned().into(),
path: "/nfs/Test Path".to_owned().into(),
monitored: Some(false),
use_season_folders: Some(false),
..EditSeriesModal::default()
};
edit_series
.quality_profile_list
.set_items(vec!["Any".to_owned(), "HD - 1080p".to_owned()]);
edit_series
.language_profile_list
.set_items(vec!["Any".to_owned(), "English".to_owned()]);
edit_series
.series_type_list
.set_items(Vec::from_iter(SeriesType::iter()));
app.data.sonarr_data.edit_series_modal = Some(edit_series);
app.data.sonarr_data.series.set_items(vec![Series {
monitored: false,
season_folder: false,
..series()
}]);
app.data.sonarr_data.quality_profile_map =
BiMap::from_iter([(1111, "Any".to_owned()), (2222, "HD - 1080p".to_owned())]);
app.data.sonarr_data.language_profiles_map =
BiMap::from_iter([(1111, "Any".to_owned()), (2222, "English".to_owned())]);
let expected_edit_series_params = EditSeriesParams {
series_id: 1,
monitored: Some(false),
use_season_folders: Some(false),
series_type: Some(SeriesType::Standard),
quality_profile_id: Some(1111),
language_profile_id: Some(1111),
root_folder_path: Some("/nfs/Test Path".to_owned()),
tag_input_string: Some("usenet, testing".to_owned()),
..EditSeriesParams::default()
};
app.push_navigation_stack(ActiveSonarrBlock::Series.into());
app.push_navigation_stack(ActiveSonarrBlock::EditSeriesPrompt.into());
app.data.sonarr_data.selected_block = BlockSelectionState::new(EDIT_SERIES_SELECTION_BLOCKS);
@@ -1274,9 +1351,9 @@ mod tests {
assert_eq!(app.get_current_route(), ActiveSonarrBlock::Series.into());
assert_eq!(
app.data.sonarr_data.prompt_confirm_action,
Some(SonarrEvent::EditSeries(None))
Some(SonarrEvent::EditSeries(expected_edit_series_params))
);
assert!(app.data.sonarr_data.edit_series_modal.is_some());
assert!(app.data.sonarr_data.edit_series_modal.is_none());
assert!(app.should_refresh);
}
}
@@ -1292,6 +1369,59 @@ mod tests {
});
}
#[test]
fn test_build_edit_series_params() {
let mut app = App::default();
let mut edit_series = EditSeriesModal {
tags: "usenet, testing".to_owned().into(),
path: "/nfs/Test Path".to_owned().into(),
monitored: Some(false),
use_season_folders: Some(false),
..EditSeriesModal::default()
};
edit_series
.quality_profile_list
.set_items(vec!["Any".to_owned(), "HD - 1080p".to_owned()]);
edit_series
.language_profile_list
.set_items(vec!["Any".to_owned(), "English".to_owned()]);
edit_series
.series_type_list
.set_items(Vec::from_iter(SeriesType::iter()));
app.data.sonarr_data.edit_series_modal = Some(edit_series);
app.data.sonarr_data.series.set_items(vec![Series {
monitored: false,
season_folder: false,
..series()
}]);
app.data.sonarr_data.quality_profile_map =
BiMap::from_iter([(1111, "Any".to_owned()), (2222, "HD - 1080p".to_owned())]);
app.data.sonarr_data.language_profiles_map =
BiMap::from_iter([(1111, "Any".to_owned()), (2222, "English".to_owned())]);
let expected_edit_series_params = EditSeriesParams {
series_id: 1,
monitored: Some(false),
use_season_folders: Some(false),
series_type: Some(SeriesType::Standard),
quality_profile_id: Some(1111),
language_profile_id: Some(1111),
root_folder_path: Some("/nfs/Test Path".to_owned()),
tag_input_string: Some("usenet, testing".to_owned()),
..EditSeriesParams::default()
};
let edit_series_params = EditSeriesHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::EditSeriesPrompt,
None,
)
.build_edit_series_params();
assert_eq!(edit_series_params, expected_edit_series_params);
assert!(app.data.sonarr_data.edit_series_modal.is_none());
}
#[test]
fn test_edit_series_handler_is_not_ready_when_loading() {
let mut app = App::default();
@@ -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();
}
@@ -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();
@@ -65,6 +65,44 @@ 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
}
fn extract_episode_id(&self) -> i64 {
self
.app
.data
.sonarr_data
.season_details_modal
.as_ref()
.expect("Season details have not been loaded")
.episodes
.current_selection()
.id
}
fn extract_series_id_season_number_tuple(&self) -> (i64, i64) {
let series_id = self.app.data.sonarr_data.series.current_selection().id;
let season_number = self
.app
.data
.sonarr_data
.seasons
.current_selection()
.season_number;
(series_id, season_number)
}
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for SeasonDetailsHandler<'a, 'b> {
@@ -233,16 +271,18 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for SeasonDetailsHandler
.push_navigation_stack(ActiveSonarrBlock::SeasonHistoryDetails.into()),
ActiveSonarrBlock::DeleteEpisodeFilePrompt => {
if self.app.data.sonarr_data.prompt_confirm {
self.app.data.sonarr_data.prompt_confirm_action =
Some(SonarrEvent::DeleteEpisodeFile(None));
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::DeleteEpisodeFile(
self.extract_episode_file_id(),
));
}
self.app.pop_navigation_stack();
}
ActiveSonarrBlock::AutomaticallySearchSeasonPrompt => {
if self.app.data.sonarr_data.prompt_confirm {
self.app.data.sonarr_data.prompt_confirm_action =
Some(SonarrEvent::TriggerAutomaticSeasonSearch(None));
self.app.data.sonarr_data.prompt_confirm_action = Some(
SonarrEvent::TriggerAutomaticSeasonSearch(self.extract_series_id_season_number_tuple()),
);
}
self.app.pop_navigation_stack();
@@ -340,8 +380,9 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for SeasonDetailsHandler
match self.active_sonarr_block {
ActiveSonarrBlock::SeasonDetails if self.key == DEFAULT_KEYBINDINGS.toggle_monitoring.key => {
self.app.data.sonarr_data.prompt_confirm = true;
self.app.data.sonarr_data.prompt_confirm_action =
Some(SonarrEvent::ToggleEpisodeMonitoring(None));
self.app.data.sonarr_data.prompt_confirm_action = Some(
SonarrEvent::ToggleEpisodeMonitoring(self.extract_episode_id()),
);
self
.app
@@ -366,15 +407,17 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for SeasonDetailsHandler
if key == DEFAULT_KEYBINDINGS.confirm.key =>
{
self.app.data.sonarr_data.prompt_confirm = true;
self.app.data.sonarr_data.prompt_confirm_action =
Some(SonarrEvent::TriggerAutomaticSeasonSearch(None));
self.app.data.sonarr_data.prompt_confirm_action = Some(
SonarrEvent::TriggerAutomaticSeasonSearch(self.extract_series_id_season_number_tuple()),
);
self.app.pop_navigation_stack();
}
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));
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::DeleteEpisodeFile(
self.extract_episode_file_id(),
));
self.app.pop_navigation_stack();
}
@@ -5,6 +5,7 @@ mod tests {
use crate::handlers::sonarr_handlers::library::season_details_handler::{
releases_sorting_options, SeasonDetailsHandler,
};
use crate::handlers::sonarr_handlers::sonarr_handler_test_utils::utils::episode;
use crate::handlers::KeyEventHandler;
use crate::models::servarr_data::sonarr::modals::SeasonDetailsModal;
use crate::models::servarr_data::sonarr::sonarr_data::sonarr_test_utils::utils::create_test_sonarr_data;
@@ -14,7 +15,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_eq, assert_str_eq};
use rstest::rstest;
use serde_json::Number;
use std::cmp::Ordering;
@@ -275,11 +276,11 @@ mod tests {
#[rstest]
#[case(
ActiveSonarrBlock::AutomaticallySearchSeasonPrompt,
SonarrEvent::TriggerAutomaticSeasonSearch(None)
SonarrEvent::TriggerAutomaticSeasonSearch((0, 0))
)]
#[case(
ActiveSonarrBlock::DeleteEpisodeFilePrompt,
SonarrEvent::DeleteEpisodeFile(None)
SonarrEvent::DeleteEpisodeFile(0)
)]
fn test_season_details_prompt_confirm_submit(
#[case] prompt_block: ActiveSonarrBlock,
@@ -550,6 +551,14 @@ mod tests {
fn test_toggle_monitoring_key() {
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.push_navigation_stack(ActiveSonarrBlock::SeasonDetails.into());
app.is_routing = false;
@@ -569,7 +578,7 @@ mod tests {
assert!(app.is_routing);
assert_eq!(
app.data.sonarr_data.prompt_confirm_action,
Some(SonarrEvent::ToggleEpisodeMonitoring(None))
Some(SonarrEvent::ToggleEpisodeMonitoring(1))
);
}
@@ -704,11 +713,11 @@ mod tests {
#[rstest]
#[case(
ActiveSonarrBlock::AutomaticallySearchSeasonPrompt,
SonarrEvent::TriggerAutomaticSeasonSearch(None)
SonarrEvent::TriggerAutomaticSeasonSearch((0, 0))
)]
#[case(
ActiveSonarrBlock::DeleteEpisodeFilePrompt,
SonarrEvent::DeleteEpisodeFile(None)
SonarrEvent::DeleteEpisodeFile(0)
)]
fn test_season_details_prompt_confirm_confirm_key(
#[case] prompt_block: ActiveSonarrBlock,
@@ -783,6 +792,85 @@ 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();
SeasonDetailsHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::SeasonDetails,
None,
)
.extract_episode_file_id();
}
#[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 = SeasonDetailsHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::SeasonDetails,
None,
)
.extract_episode_id();
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_extract_series_id_season_number_tuple() {
let mut app = App::default();
app.data.sonarr_data = create_test_sonarr_data();
let (series_id, season_number) = SeasonDetailsHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::SeasonDetails,
None,
)
.extract_series_id_season_number_tuple();
assert_eq!(series_id, 0);
assert_eq!(season_number, 0);
}
#[test]
fn test_season_details_handler_is_not_ready_when_loading() {
let mut app = App::default();
@@ -37,6 +37,23 @@ impl<'a, 'b> SeriesDetailsHandler<'a, 'b> {
.expect("Series history is undefined"),
SonarrHistoryItem
);
fn extract_series_id_season_number_tuple(&self) -> (i64, i64) {
let series_id = self.app.data.sonarr_data.series.current_selection().id;
let season_number = self
.app
.data
.sonarr_data
.seasons
.current_selection()
.season_number;
(series_id, season_number)
}
fn extract_series_id(&self) -> i64 {
self.app.data.sonarr_data.series.current_selection().id
}
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for SeriesDetailsHandler<'a, 'b> {
@@ -168,8 +185,9 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for SeriesDetailsHandler
}
ActiveSonarrBlock::AutomaticallySearchSeriesPrompt => {
if self.app.data.sonarr_data.prompt_confirm {
self.app.data.sonarr_data.prompt_confirm_action =
Some(SonarrEvent::TriggerAutomaticSeriesSearch(None));
self.app.data.sonarr_data.prompt_confirm_action = Some(
SonarrEvent::TriggerAutomaticSeriesSearch(self.extract_series_id()),
);
}
self.app.pop_navigation_stack();
@@ -177,7 +195,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for SeriesDetailsHandler
ActiveSonarrBlock::UpdateAndScanSeriesPrompt => {
if self.app.data.sonarr_data.prompt_confirm {
self.app.data.sonarr_data.prompt_confirm_action =
Some(SonarrEvent::UpdateAndScanSeries(None));
Some(SonarrEvent::UpdateAndScanSeries(self.extract_series_id()));
}
self.app.pop_navigation_stack();
@@ -259,8 +277,9 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for SeriesDetailsHandler
}
_ if key == DEFAULT_KEYBINDINGS.toggle_monitoring.key => {
self.app.data.sonarr_data.prompt_confirm = true;
self.app.data.sonarr_data.prompt_confirm_action =
Some(SonarrEvent::ToggleSeasonMonitoring(None));
self.app.data.sonarr_data.prompt_confirm_action = Some(
SonarrEvent::ToggleSeasonMonitoring(self.extract_series_id_season_number_tuple()),
);
self
.app
@@ -299,8 +318,9 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for SeriesDetailsHandler
ActiveSonarrBlock::AutomaticallySearchSeriesPrompt => {
if key == DEFAULT_KEYBINDINGS.confirm.key {
self.app.data.sonarr_data.prompt_confirm = true;
self.app.data.sonarr_data.prompt_confirm_action =
Some(SonarrEvent::TriggerAutomaticSeriesSearch(None));
self.app.data.sonarr_data.prompt_confirm_action = Some(
SonarrEvent::TriggerAutomaticSeriesSearch(self.extract_series_id()),
);
self.app.pop_navigation_stack();
}
@@ -308,7 +328,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for SeriesDetailsHandler
ActiveSonarrBlock::UpdateAndScanSeriesPrompt => {
if self.app.data.sonarr_data.prompt_confirm {
self.app.data.sonarr_data.prompt_confirm_action =
Some(SonarrEvent::UpdateAndScanSeries(None));
Some(SonarrEvent::UpdateAndScanSeries(self.extract_series_id()));
}
self.app.pop_navigation_stack();
@@ -4,6 +4,7 @@ mod tests {
use crate::app::App;
use crate::event::Key;
use crate::handlers::sonarr_handlers::library::series_details_handler::SeriesDetailsHandler;
use crate::handlers::sonarr_handlers::sonarr_handler_test_utils::utils::{season, series};
use crate::handlers::KeyEventHandler;
use crate::models::servarr_data::sonarr::sonarr_data::{
ActiveSonarrBlock, SERIES_DETAILS_BLOCKS,
@@ -11,6 +12,7 @@ mod tests {
use crate::models::sonarr_models::Season;
use crate::models::sonarr_models::SonarrHistoryItem;
use crate::models::stateful_table::StatefulTable;
use pretty_assertions::assert_eq;
use rstest::rstest;
use strum::IntoEnumIterator;
@@ -181,11 +183,11 @@ mod tests {
#[rstest]
#[case(
ActiveSonarrBlock::AutomaticallySearchSeriesPrompt,
SonarrEvent::TriggerAutomaticSeriesSearch(None)
SonarrEvent::TriggerAutomaticSeriesSearch(1)
)]
#[case(
ActiveSonarrBlock::UpdateAndScanSeriesPrompt,
SonarrEvent::UpdateAndScanSeries(None)
SonarrEvent::UpdateAndScanSeries(1)
)]
fn test_series_details_prompt_confirm_submit(
#[case] prompt_block: ActiveSonarrBlock,
@@ -193,6 +195,7 @@ mod tests {
) {
let mut app = App::default();
app.data.sonarr_data.prompt_confirm = true;
app.data.sonarr_data.series.set_items(vec![series()]);
app.push_navigation_stack(ActiveSonarrBlock::SeriesDetails.into());
app.push_navigation_stack(prompt_block.into());
@@ -398,7 +401,7 @@ mod tests {
assert!(app.is_routing);
assert_eq!(
app.data.sonarr_data.prompt_confirm_action,
Some(SonarrEvent::ToggleSeasonMonitoring(None))
Some(SonarrEvent::ToggleSeasonMonitoring((0, 0)))
);
}
@@ -565,11 +568,11 @@ mod tests {
#[rstest]
#[case(
ActiveSonarrBlock::AutomaticallySearchSeriesPrompt,
SonarrEvent::TriggerAutomaticSeriesSearch(None)
SonarrEvent::TriggerAutomaticSeriesSearch(1)
)]
#[case(
ActiveSonarrBlock::UpdateAndScanSeriesPrompt,
SonarrEvent::UpdateAndScanSeries(None)
SonarrEvent::UpdateAndScanSeries(1)
)]
fn test_series_details_prompt_confirm_confirm_key(
#[case] prompt_block: ActiveSonarrBlock,
@@ -579,6 +582,7 @@ mod tests {
) {
let mut app = App::default();
app.data.sonarr_data.prompt_confirm = true;
app.data.sonarr_data.series.set_items(vec![series()]);
app.push_navigation_stack(active_sonarr_block.into());
app.push_navigation_stack(prompt_block.into());
@@ -610,6 +614,39 @@ mod tests {
});
}
#[test]
fn test_extract_series_id_season_number_tuple() {
let mut app = App::default();
app.data.sonarr_data.series.set_items(vec![series()]);
app.data.sonarr_data.seasons.set_items(vec![season()]);
let series_id_season_number_tuple = SeriesDetailsHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::SeriesDetails,
None,
)
.extract_series_id_season_number_tuple();
assert_eq!(series_id_season_number_tuple, (1, 1));
}
#[test]
fn test_extract_series_id() {
let mut app = App::default();
app.data.sonarr_data.series.set_items(vec![series()]);
let series_id = SeriesDetailsHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::SeriesDetails,
None,
)
.extract_series_id();
assert_eq!(series_id, 1);
}
#[test]
fn test_series_details_handler_is_not_ready_when_loading() {
let mut app = App::default();
@@ -5,7 +5,7 @@ use crate::handlers::sonarr_handlers::handle_change_tab_left_right_keys;
use crate::handlers::table_handler::TableHandlingConfig;
use crate::handlers::{handle_clear_errors, handle_prompt_toggle, KeyEventHandler};
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, ROOT_FOLDERS_BLOCKS};
use crate::models::servarr_models::RootFolder;
use crate::models::servarr_models::{AddRootFolderBody, RootFolder};
use crate::models::HorizontallyScrollableText;
use crate::network::sonarr_network::SonarrEvent;
use crate::{handle_table_events, handle_text_box_keys, handle_text_box_left_right_keys};
@@ -28,6 +28,30 @@ impl<'a, 'b> RootFoldersHandler<'a, 'b> {
self.app.data.sonarr_data.root_folders,
RootFolder
);
fn build_add_root_folder_body(&mut self) -> AddRootFolderBody {
let root_folder = self
.app
.data
.sonarr_data
.edit_root_folder
.as_ref()
.unwrap()
.text
.clone();
self.app.data.sonarr_data.edit_root_folder = None;
AddRootFolderBody { path: root_folder }
}
fn extract_root_folder_id(&self) -> i64 {
self
.app
.data
.sonarr_data
.root_folders
.current_selection()
.id
}
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for RootFoldersHandler<'a, 'b> {
@@ -124,7 +148,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for RootFoldersHandler<'
ActiveSonarrBlock::DeleteRootFolderPrompt => {
if self.app.data.sonarr_data.prompt_confirm {
self.app.data.sonarr_data.prompt_confirm_action =
Some(SonarrEvent::DeleteRootFolder(None));
Some(SonarrEvent::DeleteRootFolder(self.extract_root_folder_id()));
}
self.app.pop_navigation_stack();
@@ -140,7 +164,9 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for RootFoldersHandler<'
.text
.is_empty() =>
{
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::AddRootFolder(None));
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::AddRootFolder(
self.build_add_root_folder_body(),
));
self.app.data.sonarr_data.prompt_confirm = true;
self.app.should_ignore_quit_key = false;
self.app.pop_navigation_stack();
@@ -192,7 +218,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for RootFoldersHandler<'
if key == DEFAULT_KEYBINDINGS.confirm.key {
self.app.data.sonarr_data.prompt_confirm = true;
self.app.data.sonarr_data.prompt_confirm_action =
Some(SonarrEvent::DeleteRootFolder(None));
Some(SonarrEvent::DeleteRootFolder(self.extract_root_folder_id()));
self.app.pop_navigation_stack();
}
@@ -1,14 +1,16 @@
#[cfg(test)]
mod tests {
use pretty_assertions::assert_eq;
use strum::IntoEnumIterator;
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::app::App;
use crate::event::Key;
use crate::handlers::sonarr_handlers::root_folders::RootFoldersHandler;
use crate::handlers::sonarr_handlers::sonarr_handler_test_utils::utils::root_folder;
use crate::handlers::KeyEventHandler;
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, ROOT_FOLDERS_BLOCKS};
use crate::models::servarr_models::RootFolder;
use crate::models::servarr_models::{AddRootFolderBody, RootFolder};
use crate::models::HorizontallyScrollableText;
mod test_handle_home_end {
@@ -257,6 +259,9 @@ mod tests {
#[test]
fn test_add_root_folder_prompt_confirm_submit() {
let mut app = App::default();
let expected_add_root_folder_body = AddRootFolderBody {
path: "Test".to_owned(),
};
app
.data
.sonarr_data
@@ -280,12 +285,13 @@ mod tests {
assert!(!app.should_ignore_quit_key);
assert_eq!(
app.data.sonarr_data.prompt_confirm_action,
Some(SonarrEvent::AddRootFolder(None))
Some(SonarrEvent::AddRootFolder(expected_add_root_folder_body))
);
assert_eq!(
app.get_current_route(),
ActiveSonarrBlock::RootFolders.into()
);
assert!(app.data.sonarr_data.edit_root_folder.is_none());
}
#[test]
@@ -321,7 +327,7 @@ mod tests {
.data
.sonarr_data
.root_folders
.set_items(vec![RootFolder::default()]);
.set_items(vec![root_folder()]);
app.data.sonarr_data.prompt_confirm = true;
app.push_navigation_stack(ActiveSonarrBlock::RootFolders.into());
app.push_navigation_stack(ActiveSonarrBlock::DeleteRootFolderPrompt.into());
@@ -337,7 +343,7 @@ mod tests {
assert!(app.data.sonarr_data.prompt_confirm);
assert_eq!(
app.data.sonarr_data.prompt_confirm_action,
Some(SonarrEvent::DeleteRootFolder(None))
Some(SonarrEvent::DeleteRootFolder(1))
);
assert_eq!(
app.get_current_route(),
@@ -614,7 +620,7 @@ mod tests {
.data
.sonarr_data
.root_folders
.set_items(vec![RootFolder::default()]);
.set_items(vec![root_folder()]);
app.push_navigation_stack(ActiveSonarrBlock::RootFolders.into());
app.push_navigation_stack(ActiveSonarrBlock::DeleteRootFolderPrompt.into());
@@ -629,7 +635,7 @@ mod tests {
assert!(app.data.sonarr_data.prompt_confirm);
assert_eq!(
app.data.sonarr_data.prompt_confirm_action,
Some(SonarrEvent::DeleteRootFolder(None))
Some(SonarrEvent::DeleteRootFolder(1))
);
assert_eq!(
app.get_current_route(),
@@ -649,6 +655,46 @@ mod tests {
})
}
#[test]
fn test_extract_root_folder_id() {
let mut app = App::default();
app
.data
.sonarr_data
.root_folders
.set_items(vec![root_folder()]);
let root_folder_id = RootFoldersHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::DeleteRootFolderPrompt,
None,
)
.extract_root_folder_id();
assert_eq!(root_folder_id, 1);
}
#[test]
fn test_build_add_root_folder_body() {
let mut app = App::default();
app.data.sonarr_data.edit_root_folder = Some("/nfs/test".into());
let expected_add_root_folder_body = AddRootFolderBody {
path: "/nfs/test".to_owned(),
};
let root_folder = RootFoldersHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::AddRootFolderPrompt,
None,
)
.build_add_root_folder_body();
assert_eq!(root_folder, expected_add_root_folder_body);
assert!(app.data.sonarr_data.edit_root_folder.is_none());
}
#[test]
fn test_root_folders_handler_not_ready_when_loading() {
let mut app = App::default();
@@ -1,6 +1,17 @@
#[cfg(test)]
#[macro_use]
mod utils {
pub(in crate::handlers::sonarr_handlers) mod utils {
use crate::models::servarr_models::{
Indexer, IndexerField, Language, Quality, QualityWrapper, RootFolder,
};
use crate::models::sonarr_models::{
AddSeriesSearchResult, AddSeriesSearchResultStatistics, DownloadRecord, DownloadStatus,
Episode, EpisodeFile, IndexerSettings, MediaInfo, Rating, Season, SeasonStatistics, Series,
SeriesStatistics, SeriesStatus, SeriesType,
};
use crate::models::HorizontallyScrollableText;
use chrono::DateTime;
use serde_json::{json, Number};
#[macro_export]
macro_rules! test_edit_series_key {
@@ -154,4 +165,230 @@ mod utils {
);
};
}
pub fn add_series_search_result() -> AddSeriesSearchResult {
AddSeriesSearchResult {
tvdb_id: 1234,
title: HorizontallyScrollableText::from("Test"),
status: Some("continuing".to_owned()),
ended: false,
overview: Some("New series blah blah blah".to_owned()),
genres: genres(),
year: 2023,
network: Some("Prime Video".to_owned()),
runtime: 60,
ratings: Some(rating()),
statistics: Some(add_series_search_result_statistics()),
}
}
pub fn add_series_search_result_statistics() -> AddSeriesSearchResultStatistics {
AddSeriesSearchResultStatistics { season_count: 3 }
}
pub fn download_record() -> DownloadRecord {
DownloadRecord {
title: "Test Download Title".to_owned(),
status: DownloadStatus::Downloading,
id: 1,
episode_id: 1,
size: 3543348019f64,
sizeleft: 1771674009f64,
output_path: Some(HorizontallyScrollableText::from(
"/nfs/tv/Test show/season 1/",
)),
indexer: "kickass torrents".to_owned(),
download_client: Some("transmission".to_owned()),
}
}
pub fn episode() -> Episode {
Episode {
id: 1,
series_id: 1,
tvdb_id: 1234,
episode_file_id: 1,
season_number: 1,
episode_number: 1,
title: "Something cool".to_owned(),
air_date_utc: Some(DateTime::from(
DateTime::parse_from_rfc3339("2024-02-10T07:28:45Z").unwrap(),
)),
overview: Some("Okay so this one time at band camp...".to_owned()),
has_file: true,
monitored: true,
episode_file: Some(episode_file()),
}
}
pub fn episode_file() -> EpisodeFile {
EpisodeFile {
id: 1,
relative_path: "/season 1/episode 1.mkv".to_owned(),
path: "/nfs/tv/series/season 1/episode 1.mkv".to_owned(),
size: 3543348019,
quality: quality_wrapper(),
languages: vec![language()],
date_added: DateTime::from(DateTime::parse_from_rfc3339("2024-02-10T07:28:45Z").unwrap()),
media_info: Some(media_info()),
}
}
pub fn genres() -> Vec<String> {
vec!["cool".to_owned(), "family".to_owned(), "fun".to_owned()]
}
pub fn indexer() -> Indexer {
Indexer {
enable_rss: true,
enable_automatic_search: true,
enable_interactive_search: true,
supports_rss: true,
supports_search: true,
protocol: "torrent".to_owned(),
priority: 25,
download_client_id: 0,
name: Some("Test Indexer".to_owned()),
implementation_name: Some("Torznab".to_owned()),
implementation: Some("Torznab".to_owned()),
config_contract: Some("TorznabSettings".to_owned()),
tags: vec![Number::from(1)],
id: 1,
fields: Some(vec![
IndexerField {
name: Some("baseUrl".to_owned()),
value: Some(json!("https://test.com")),
},
IndexerField {
name: Some("apiKey".to_owned()),
value: Some(json!("")),
},
IndexerField {
name: Some("seedCriteria.seedRatio".to_owned()),
value: Some(json!("1.2")),
},
]),
}
}
pub fn indexer_settings() -> IndexerSettings {
IndexerSettings {
id: 1,
minimum_age: 1,
retention: 1,
maximum_size: 12345,
rss_sync_interval: 60,
}
}
pub fn language() -> Language {
Language {
id: 1,
name: "English".to_owned(),
}
}
pub fn media_info() -> MediaInfo {
MediaInfo {
audio_bitrate: 0,
audio_channels: Number::from_f64(7.1).unwrap(),
audio_codec: Some("AAC".to_owned()),
audio_languages: Some("eng".to_owned()),
audio_stream_count: 1,
video_bit_depth: 10,
video_bitrate: 0,
video_codec: "x265".to_owned(),
video_fps: Number::from_f64(23.976).unwrap(),
resolution: "1920x1080".to_owned(),
run_time: "23:51".to_owned(),
scan_type: "Progressive".to_owned(),
subtitles: Some("English".to_owned()),
}
}
pub fn quality() -> Quality {
Quality {
name: "Bluray-1080p".to_owned(),
}
}
pub fn quality_wrapper() -> QualityWrapper {
QualityWrapper { quality: quality() }
}
pub fn rating() -> Rating {
Rating {
votes: 406744,
value: 8.4,
}
}
pub fn season() -> Season {
Season {
title: None,
season_number: 1,
monitored: true,
statistics: season_statistics(),
}
}
pub fn season_statistics() -> SeasonStatistics {
SeasonStatistics {
previous_airing: Some(DateTime::from(
DateTime::parse_from_rfc3339("2022-10-24T01:00:00Z").unwrap(),
)),
next_airing: None,
episode_file_count: 10,
episode_count: 10,
total_episode_count: 10,
size_on_disk: 36708563419,
percent_of_episodes: 100.0,
}
}
pub fn series() -> Series {
Series {
title: "Test".to_owned().into(),
status: SeriesStatus::Continuing,
ended: false,
overview: Some("Blah blah blah".to_owned()),
network: Some("HBO".to_owned()),
seasons: Some(vec![season()]),
year: 2022,
path: "/nfs/tv/Test".to_owned(),
quality_profile_id: 6,
language_profile_id: 1,
season_folder: true,
monitored: true,
runtime: 63,
tvdb_id: 371572,
series_type: SeriesType::Standard,
certification: Some("TV-MA".to_owned()),
genres: vec!["cool".to_owned(), "family".to_owned(), "fun".to_owned()],
tags: vec![Number::from(3)],
ratings: rating(),
statistics: Some(series_statistics()),
id: 1,
}
}
pub fn series_statistics() -> SeriesStatistics {
SeriesStatistics {
season_count: 2,
episode_file_count: 18,
episode_count: 18,
total_episode_count: 50,
size_on_disk: 63894022699,
percent_of_episodes: 100.0,
}
}
pub fn root_folder() -> RootFolder {
RootFolder {
id: 1,
path: "/nfs".to_owned(),
accessible: true,
free_space: 219902325555200,
unmapped_folders: None,
}
}
}
@@ -3,6 +3,7 @@ use crate::app::App;
use crate::event::Key;
use crate::handlers::{handle_prompt_toggle, KeyEventHandler};
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, SYSTEM_DETAILS_BLOCKS};
use crate::models::sonarr_models::SonarrTaskName;
use crate::models::stateful_list::StatefulList;
use crate::models::Scrollable;
use crate::network::sonarr_network::SonarrEvent;
@@ -18,6 +19,18 @@ pub(super) struct SystemDetailsHandler<'a, 'b> {
_context: Option<ActiveSonarrBlock>,
}
impl<'a, 'b> SystemDetailsHandler<'a, 'b> {
fn extract_task_name(&self) -> SonarrTaskName {
self
.app
.data
.sonarr_data
.tasks
.current_selection()
.task_name
}
}
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for SystemDetailsHandler<'a, 'b> {
fn accepts(active_block: ActiveSonarrBlock) -> bool {
SYSTEM_DETAILS_BLOCKS.contains(&active_block)
@@ -137,7 +150,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for SystemDetailsHandler
}
ActiveSonarrBlock::SystemTaskStartConfirmPrompt => {
if self.app.data.sonarr_data.prompt_confirm {
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::StartTask(None));
self.app.data.sonarr_data.prompt_confirm_action =
Some(SonarrEvent::StartTask(self.extract_task_name()));
}
self.app.pop_navigation_stack();
@@ -174,7 +188,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for SystemDetailsHandler
&& self.key == DEFAULT_KEYBINDINGS.confirm.key
{
self.app.data.sonarr_data.prompt_confirm = true;
self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::StartTask(None));
self.app.data.sonarr_data.prompt_confirm_action =
Some(SonarrEvent::StartTask(self.extract_task_name()));
self.app.pop_navigation_stack();
}
}
@@ -1,6 +1,6 @@
#[cfg(test)]
mod tests {
use pretty_assertions::assert_str_eq;
use pretty_assertions::{assert_eq, assert_str_eq};
use strum::IntoEnumIterator;
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
@@ -12,10 +12,11 @@ mod tests {
ActiveSonarrBlock, SYSTEM_DETAILS_BLOCKS,
};
use crate::models::servarr_models::QueueEvent;
use crate::models::sonarr_models::SonarrTask;
use crate::models::sonarr_models::{SonarrTask, SonarrTaskName};
use crate::models::{HorizontallyScrollableText, ScrollableText};
mod test_handle_scroll_up_and_down {
use pretty_assertions::assert_eq;
use rstest::rstest;
use crate::models::{HorizontallyScrollableText, ScrollableText};
@@ -245,6 +246,7 @@ mod tests {
use crate::{extended_stateful_iterable_vec, test_iterable_home_and_end};
use super::*;
use pretty_assertions::assert_eq;
test_iterable_home_and_end!(
test_log_details_home_end,
@@ -693,6 +695,11 @@ mod tests {
let mut app = App::default();
app.data.sonarr_data.updates = ScrollableText::with_string("Test".to_owned());
app.data.sonarr_data.prompt_confirm = true;
app
.data
.sonarr_data
.tasks
.set_items(vec![SonarrTask::default()]);
app.push_navigation_stack(ActiveSonarrBlock::SystemTasks.into());
app.push_navigation_stack(ActiveSonarrBlock::SystemTaskStartConfirmPrompt.into());
@@ -707,7 +714,7 @@ mod tests {
assert!(app.data.sonarr_data.prompt_confirm);
assert_eq!(
app.data.sonarr_data.prompt_confirm_action,
Some(SonarrEvent::StartTask(None))
Some(SonarrEvent::StartTask(SonarrTaskName::default()))
);
assert_eq!(
app.get_current_route(),
@@ -848,6 +855,7 @@ mod tests {
}
mod test_handle_key_char {
use pretty_assertions::assert_eq;
use rstest::rstest;
use crate::network::sonarr_network::SonarrEvent;
@@ -914,6 +922,11 @@ mod tests {
let mut app = App::default();
app.push_navigation_stack(ActiveSonarrBlock::System.into());
app.data.sonarr_data.updates = ScrollableText::with_string("Test".to_owned());
app
.data
.sonarr_data
.tasks
.set_items(vec![SonarrTask::default()]);
app.push_navigation_stack(ActiveSonarrBlock::SystemTasks.into());
app.push_navigation_stack(ActiveSonarrBlock::SystemTaskStartConfirmPrompt.into());
@@ -928,7 +941,7 @@ mod tests {
assert!(app.data.sonarr_data.prompt_confirm);
assert_eq!(
app.data.sonarr_data.prompt_confirm_action,
Some(SonarrEvent::StartTask(None))
Some(SonarrEvent::StartTask(SonarrTaskName::default()))
);
assert_eq!(
app.get_current_route(),
@@ -948,6 +961,26 @@ mod tests {
})
}
#[test]
fn test_extract_task_name() {
let mut app = App::default();
app
.data
.sonarr_data
.tasks
.set_items(vec![SonarrTask::default()]);
let task_name = SystemDetailsHandler::with(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveSonarrBlock::SystemTasks,
None,
)
.extract_task_name();
assert_eq!(task_name, SonarrTaskName::default());
}
#[test]
fn test_system_details_handler_not_ready_when_loading() {
let mut app = App::default();
+5 -1
View File
@@ -29,6 +29,8 @@ pub struct AddMovieBody {
pub minimum_availability: String,
pub monitored: bool,
pub tags: Vec<i64>,
#[serde(skip_serializing, skip_deserializing)]
pub tag_input_string: Option<String>,
pub add_options: AddMovieOptions,
}
@@ -135,7 +137,7 @@ pub enum CreditType {
Crew,
}
#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq, Default)]
#[serde(rename_all = "lowercase")]
pub struct DeleteMovieParams {
pub id: i64,
@@ -188,6 +190,8 @@ pub struct EditMovieParams {
pub quality_profile_id: Option<i64>,
pub root_folder_path: Option<String>,
pub tags: Option<Vec<i64>>,
#[serde(skip_serializing, skip_deserializing)]
pub tag_input_string: Option<String>,
pub clear_tags: bool,
}
+3 -1
View File
@@ -11,7 +11,7 @@ use super::HorizontallyScrollableText;
#[path = "servarr_models_tests.rs"]
mod servarr_models_tests;
#[derive(Default, Serialize, Debug)]
#[derive(Default, Serialize, Debug, Clone, PartialEq, Eq)]
pub struct AddRootFolderBody {
pub path: String,
}
@@ -101,6 +101,8 @@ pub struct EditIndexerParams {
pub api_key: Option<String>,
pub seed_ratio: Option<String>,
pub tags: Option<Vec<i64>>,
#[serde(skip_serializing, skip_deserializing)]
pub tag_input_string: Option<String>,
pub priority: Option<i64>,
pub clear_tags: bool,
}
+5 -1
View File
@@ -34,6 +34,8 @@ pub struct AddSeriesBody {
pub series_type: String,
pub season_folder: bool,
pub tags: Vec<i64>,
#[serde(skip_serializing, skip_deserializing)]
pub tag_input_string: Option<String>,
pub add_options: AddSeriesOptions,
}
@@ -95,7 +97,7 @@ pub struct BlocklistResponse {
pub records: Vec<BlocklistItem>,
}
#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
#[derive(Serialize, Deserialize, Debug, Default, Clone, Eq, PartialEq)]
#[serde(rename_all = "lowercase")]
pub struct DeleteSeriesParams {
pub id: i64,
@@ -192,6 +194,8 @@ pub struct EditSeriesParams {
pub series_type: Option<SeriesType>,
pub root_folder_path: Option<String>,
pub tags: Option<Vec<i64>>,
#[serde(skip_serializing, skip_deserializing)]
pub tag_input_string: Option<String>,
pub clear_tags: bool,
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff