feat: Added CLI and TUI support for editing Lidarr artists

This commit is contained in:
2026-01-07 12:01:03 -07:00
parent 3c1634d1e3
commit b1afdaf541
49 changed files with 2338 additions and 296 deletions
@@ -1,3 +1,4 @@
use crate::models::Route;
use crate::models::lidarr_models::DeleteArtistParams; use crate::models::lidarr_models::DeleteArtistParams;
use crate::network::lidarr_network::LidarrEvent; use crate::network::lidarr_network::LidarrEvent;
use crate::{ use crate::{
@@ -7,7 +8,6 @@ use crate::{
matches_key, matches_key,
models::servarr_data::lidarr::lidarr_data::{ActiveLidarrBlock, DELETE_ARTIST_BLOCKS}, models::servarr_data::lidarr::lidarr_data::{ActiveLidarrBlock, DELETE_ARTIST_BLOCKS},
}; };
use crate::models::Route;
#[cfg(test)] #[cfg(test)]
#[path = "delete_artist_handler_tests.rs"] #[path = "delete_artist_handler_tests.rs"]
@@ -1,10 +1,10 @@
use crate::app::App; use crate::app::App;
use crate::event::Key; use crate::event::Key;
use crate::handlers::{KeyEventHandler, handle_prompt_toggle}; use crate::handlers::{KeyEventHandler, handle_prompt_toggle};
use crate::models::{Route, Scrollable};
use crate::models::lidarr_models::EditArtistParams; use crate::models::lidarr_models::EditArtistParams;
use crate::models::servarr_data::lidarr::lidarr_data::{ActiveLidarrBlock, EDIT_ARTIST_BLOCKS}; use crate::models::servarr_data::lidarr::lidarr_data::{ActiveLidarrBlock, EDIT_ARTIST_BLOCKS};
use crate::models::servarr_data::lidarr::modals::EditArtistModal; use crate::models::servarr_data::lidarr::modals::EditArtistModal;
use crate::models::{Route, Scrollable};
use crate::network::lidarr_network::LidarrEvent; use crate::network::lidarr_network::LidarrEvent;
use crate::{handle_text_box_keys, handle_text_box_left_right_keys, matches_key}; use crate::{handle_text_box_keys, handle_text_box_left_right_keys, matches_key};
File diff suppressed because it is too large Load Diff
@@ -3,6 +3,7 @@ mod tests {
use std::cmp::Ordering; use std::cmp::Ordering;
use pretty_assertions::{assert_eq, assert_str_eq}; use pretty_assertions::{assert_eq, assert_str_eq};
use rstest::rstest;
use serde_json::Number; use serde_json::Number;
use strum::IntoEnumIterator; use strum::IntoEnumIterator;
@@ -11,9 +12,15 @@ mod tests {
use crate::handlers::KeyEventHandler; use crate::handlers::KeyEventHandler;
use crate::handlers::lidarr_handlers::library::{LibraryHandler, artists_sorting_options}; use crate::handlers::lidarr_handlers::library::{LibraryHandler, artists_sorting_options};
use crate::models::lidarr_models::{Artist, ArtistStatistics, ArtistStatus}; use crate::models::lidarr_models::{Artist, ArtistStatistics, ArtistStatus};
use crate::models::servarr_data::lidarr::lidarr_data::{ActiveLidarrBlock, DELETE_ARTIST_BLOCKS, EDIT_ARTIST_BLOCKS, LIBRARY_BLOCKS}; use crate::models::servarr_data::lidarr::lidarr_data::{
ActiveLidarrBlock, DELETE_ARTIST_BLOCKS, EDIT_ARTIST_BLOCKS, EDIT_ARTIST_SELECTION_BLOCKS,
LIBRARY_BLOCKS,
};
use crate::models::servarr_data::lidarr::modals::EditArtistModal;
use crate::network::lidarr_network::LidarrEvent; use crate::network::lidarr_network::LidarrEvent;
use crate::{assert_modal_absent, assert_navigation_popped, assert_navigation_pushed}; use crate::{
assert_modal_absent, assert_modal_present, assert_navigation_popped, assert_navigation_pushed,
};
#[test] #[test]
fn test_library_handler_accepts() { fn test_library_handler_accepts() {
@@ -494,4 +501,134 @@ mod tests {
}, },
] ]
} }
#[test]
fn test_delegates_delete_artist_blocks_to_delete_artist_handler() {
let mut app = App::test_default();
app
.data
.lidarr_data
.artists
.set_items(vec![Artist::default()]);
app.push_navigation_stack(ActiveLidarrBlock::Artists.into());
app.push_navigation_stack(ActiveLidarrBlock::DeleteArtistPrompt.into());
LibraryHandler::new(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
ActiveLidarrBlock::DeleteArtistPrompt,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveLidarrBlock::Artists.into());
}
#[rstest]
fn test_delegates_edit_artist_blocks_to_edit_artist_handler(
#[values(
ActiveLidarrBlock::EditArtistPrompt,
ActiveLidarrBlock::EditArtistSelectMetadataProfile,
ActiveLidarrBlock::EditArtistSelectMonitorNewItems,
ActiveLidarrBlock::EditArtistSelectQualityProfile,
ActiveLidarrBlock::EditArtistTagsInput,
ActiveLidarrBlock::EditArtistPathInput,
)]
active_lidarr_block: ActiveLidarrBlock,
) {
let mut app = App::test_default();
app
.data
.lidarr_data
.artists
.set_items(vec![Artist::default()]);
app.data.lidarr_data.edit_artist_modal = Some(EditArtistModal::default());
app.push_navigation_stack(ActiveLidarrBlock::Artists.into());
app.push_navigation_stack(active_lidarr_block.into());
LibraryHandler::new(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
active_lidarr_block,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveLidarrBlock::Artists.into());
}
#[test]
fn test_edit_key() {
let mut app = App::test_default();
app
.data
.lidarr_data
.artists
.set_items(vec![Artist::default()]);
app.data.lidarr_data.quality_profile_map =
bimap::BiMap::from_iter([(0i64, "Default Quality".to_owned())]);
app.data.lidarr_data.metadata_profile_map =
bimap::BiMap::from_iter([(0i64, "Default Metadata".to_owned())]);
app.push_navigation_stack(ActiveLidarrBlock::Artists.into());
LibraryHandler::new(
DEFAULT_KEYBINDINGS.edit.key,
&mut app,
ActiveLidarrBlock::Artists,
None,
)
.handle();
assert_navigation_pushed!(app, ActiveLidarrBlock::EditArtistPrompt.into());
assert_modal_present!(app.data.lidarr_data.edit_artist_modal);
assert_eq!(
app.data.lidarr_data.selected_block.blocks,
EDIT_ARTIST_SELECTION_BLOCKS
);
}
#[test]
fn test_edit_key_no_op_when_not_ready() {
let mut app = App::test_default();
app.is_loading = true;
app.push_navigation_stack(ActiveLidarrBlock::Artists.into());
app
.data
.lidarr_data
.artists
.set_items(vec![Artist::default()]);
LibraryHandler::new(
DEFAULT_KEYBINDINGS.edit.key,
&mut app,
ActiveLidarrBlock::Artists,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveLidarrBlock::Artists.into());
assert_modal_absent!(app.data.lidarr_data.edit_artist_modal);
}
#[test]
fn test_refresh_key() {
let mut app = App::test_default();
app
.data
.lidarr_data
.artists
.set_items(vec![Artist::default()]);
app.push_navigation_stack(ActiveLidarrBlock::Artists.into());
LibraryHandler::new(
DEFAULT_KEYBINDINGS.refresh.key,
&mut app,
ActiveLidarrBlock::Artists,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveLidarrBlock::Artists.into());
assert!(app.should_refresh);
}
} }
+4 -5
View File
@@ -10,7 +10,6 @@ use crate::{
ActiveLidarrBlock, DELETE_ARTIST_SELECTION_BLOCKS, EDIT_ARTIST_SELECTION_BLOCKS, ActiveLidarrBlock, DELETE_ARTIST_SELECTION_BLOCKS, EDIT_ARTIST_SELECTION_BLOCKS,
LIBRARY_BLOCKS, LIBRARY_BLOCKS,
}, },
servarr_data::lidarr::modals::EditArtistModal,
stateful_table::SortOption, stateful_table::SortOption,
}, },
network::lidarr_network::LidarrEvent, network::lidarr_network::LidarrEvent,
@@ -22,9 +21,9 @@ use crate::handlers::table_handler::{TableHandlingConfig, handle_table};
mod delete_artist_handler; mod delete_artist_handler;
mod edit_artist_handler; mod edit_artist_handler;
use crate::models::Route;
pub(in crate::handlers::lidarr_handlers) use delete_artist_handler::DeleteArtistHandler; pub(in crate::handlers::lidarr_handlers) use delete_artist_handler::DeleteArtistHandler;
pub(in crate::handlers::lidarr_handlers) use edit_artist_handler::EditArtistHandler; pub(in crate::handlers::lidarr_handlers) use edit_artist_handler::EditArtistHandler;
use crate::models::Route;
#[cfg(test)] #[cfg(test)]
#[path = "library_handler_tests.rs"] #[path = "library_handler_tests.rs"]
@@ -66,7 +65,8 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveLidarrBlock> for LibraryHandler<'a, '
.handle(); .handle();
} }
_ if EditArtistHandler::accepts(self.active_lidarr_block) => { _ if EditArtistHandler::accepts(self.active_lidarr_block) => {
EditArtistHandler::new(self.key, self.app, self.active_lidarr_block, self.context).handle(); EditArtistHandler::new(self.key, self.app, self.active_lidarr_block, self.context)
.handle();
} }
_ => self.handle_key_event(), _ => self.handle_key_event(),
} }
@@ -168,8 +168,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveLidarrBlock> for LibraryHandler<'a, '
.pop_and_push_navigation_stack(self.active_lidarr_block.into()); .pop_and_push_navigation_stack(self.active_lidarr_block.into());
} }
_ if matches_key!(edit, key) => { _ if matches_key!(edit, key) => {
self.app.data.lidarr_data.edit_artist_modal = self.app.data.lidarr_data.edit_artist_modal = Some((&self.app.data.lidarr_data).into());
Some((&self.app.data.lidarr_data).into());
self self
.app .app
.push_navigation_stack(ActiveLidarrBlock::EditArtistPrompt.into()); .push_navigation_stack(ActiveLidarrBlock::EditArtistPrompt.into());
@@ -1,12 +1,15 @@
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use rstest::rstest;
use strum::IntoEnumIterator;
use crate::app::App; use crate::app::App;
use crate::app::key_binding::DEFAULT_KEYBINDINGS; use crate::app::key_binding::DEFAULT_KEYBINDINGS;
use crate::handlers::KeyEventHandler; use crate::handlers::KeyEventHandler;
use crate::handlers::lidarr_handlers::LidarrHandler; use crate::handlers::lidarr_handlers::LidarrHandler;
use crate::models::lidarr_models::Artist;
use crate::models::servarr_data::lidarr::lidarr_data::ActiveLidarrBlock; use crate::models::servarr_data::lidarr::lidarr_data::ActiveLidarrBlock;
use crate::models::servarr_data::lidarr::modals::EditArtistModal;
use pretty_assertions::assert_eq;
use rstest::rstest;
use strum::IntoEnumIterator;
#[rstest] #[rstest]
fn test_lidarr_handler_ignore_special_keys( fn test_lidarr_handler_ignore_special_keys(
@@ -48,4 +51,45 @@ mod tests {
assert!(LidarrHandler::accepts(lidarr_block)); assert!(LidarrHandler::accepts(lidarr_block));
} }
} }
#[rstest]
fn test_delegates_library_blocks_to_library_handler(
#[values(
ActiveLidarrBlock::Artists,
ActiveLidarrBlock::ArtistsSortPrompt,
ActiveLidarrBlock::FilterArtists,
ActiveLidarrBlock::FilterArtistsError,
ActiveLidarrBlock::SearchArtists,
ActiveLidarrBlock::SearchArtistsError,
ActiveLidarrBlock::UpdateAllArtistsPrompt,
ActiveLidarrBlock::DeleteArtistPrompt,
ActiveLidarrBlock::EditArtistPrompt,
ActiveLidarrBlock::EditArtistPathInput,
ActiveLidarrBlock::EditArtistSelectMetadataProfile,
ActiveLidarrBlock::EditArtistSelectMonitorNewItems,
ActiveLidarrBlock::EditArtistSelectQualityProfile,
ActiveLidarrBlock::EditArtistTagsInput
)]
active_lidarr_block: ActiveLidarrBlock,
) {
let mut app = App::test_default();
app
.data
.lidarr_data
.artists
.set_items(vec![Artist::default()]);
app.data.lidarr_data.edit_artist_modal = Some(EditArtistModal::default());
app.push_navigation_stack(ActiveLidarrBlock::Artists.into());
app.push_navigation_stack(active_lidarr_block.into());
LidarrHandler::new(
DEFAULT_KEYBINDINGS.esc.key,
&mut app,
active_lidarr_block,
None,
)
.handle();
assert_eq!(app.get_current_route(), ActiveLidarrBlock::Artists.into());
}
} }
+2 -2
View File
@@ -1,10 +1,10 @@
use library::LibraryHandler; use library::LibraryHandler;
use super::KeyEventHandler;
use crate::models::Route;
use crate::{ use crate::{
app::App, event::Key, matches_key, models::servarr_data::lidarr::lidarr_data::ActiveLidarrBlock, app::App, event::Key, matches_key, models::servarr_data::lidarr::lidarr_data::ActiveLidarrBlock,
}; };
use crate::models::Route;
use super::KeyEventHandler;
mod library; mod library;
@@ -4,8 +4,8 @@ use crate::handlers::radarr_handlers::handle_change_tab_left_right_keys;
use crate::handlers::table_handler::{TableHandlingConfig, handle_table}; use crate::handlers::table_handler::{TableHandlingConfig, handle_table};
use crate::handlers::{KeyEventHandler, handle_clear_errors, handle_prompt_toggle}; use crate::handlers::{KeyEventHandler, handle_clear_errors, handle_prompt_toggle};
use crate::matches_key; use crate::matches_key;
use crate::models::radarr_models::BlocklistItem;
use crate::models::Route; use crate::models::Route;
use crate::models::radarr_models::BlocklistItem;
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, BLOCKLIST_BLOCKS}; use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, BLOCKLIST_BLOCKS};
use crate::models::stateful_table::SortOption; use crate::models::stateful_table::SortOption;
use crate::network::radarr_network::RadarrEvent; use crate::network::radarr_network::RadarrEvent;
@@ -3,12 +3,12 @@ use crate::event::Key;
use crate::handlers::KeyEventHandler; use crate::handlers::KeyEventHandler;
use crate::handlers::table_handler::{TableHandlingConfig, handle_table}; use crate::handlers::table_handler::{TableHandlingConfig, handle_table};
use crate::matches_key; use crate::matches_key;
use crate::models::{BlockSelectionState, Route};
use crate::models::servarr_data::radarr::radarr_data::{ use crate::models::servarr_data::radarr::radarr_data::{
ADD_MOVIE_SELECTION_BLOCKS, ActiveRadarrBlock, COLLECTION_DETAILS_BLOCKS, ADD_MOVIE_SELECTION_BLOCKS, ActiveRadarrBlock, COLLECTION_DETAILS_BLOCKS,
EDIT_COLLECTION_SELECTION_BLOCKS, EDIT_COLLECTION_SELECTION_BLOCKS,
}; };
use crate::models::stateful_table::StatefulTable; use crate::models::stateful_table::StatefulTable;
use crate::models::{BlockSelectionState, Route};
#[cfg(test)] #[cfg(test)]
#[path = "collection_details_handler_tests.rs"] #[path = "collection_details_handler_tests.rs"]
@@ -1,10 +1,10 @@
use crate::app::App; use crate::app::App;
use crate::event::Key; use crate::event::Key;
use crate::handlers::{KeyEventHandler, handle_prompt_toggle}; use crate::handlers::{KeyEventHandler, handle_prompt_toggle};
use crate::models::{Route, Scrollable};
use crate::models::radarr_models::EditCollectionParams; use crate::models::radarr_models::EditCollectionParams;
use crate::models::servarr_data::radarr::modals::EditCollectionModal; use crate::models::servarr_data::radarr::modals::EditCollectionModal;
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, EDIT_COLLECTION_BLOCKS}; use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, EDIT_COLLECTION_BLOCKS};
use crate::models::{Route, Scrollable};
use crate::network::radarr_network::RadarrEvent; use crate::network::radarr_network::RadarrEvent;
use crate::{handle_text_box_keys, handle_text_box_left_right_keys, matches_key}; use crate::{handle_text_box_keys, handle_text_box_left_right_keys, matches_key};
@@ -6,12 +6,12 @@ use crate::handlers::radarr_handlers::handle_change_tab_left_right_keys;
use crate::handlers::table_handler::{TableHandlingConfig, handle_table}; use crate::handlers::table_handler::{TableHandlingConfig, handle_table};
use crate::handlers::{KeyEventHandler, handle_clear_errors, handle_prompt_toggle}; use crate::handlers::{KeyEventHandler, handle_clear_errors, handle_prompt_toggle};
use crate::matches_key; use crate::matches_key;
use crate::models::{BlockSelectionState, Route};
use crate::models::radarr_models::Collection; use crate::models::radarr_models::Collection;
use crate::models::servarr_data::radarr::radarr_data::{ use crate::models::servarr_data::radarr::radarr_data::{
ActiveRadarrBlock, COLLECTIONS_BLOCKS, EDIT_COLLECTION_SELECTION_BLOCKS, ActiveRadarrBlock, COLLECTIONS_BLOCKS, EDIT_COLLECTION_SELECTION_BLOCKS,
}; };
use crate::models::stateful_table::SortOption; use crate::models::stateful_table::SortOption;
use crate::models::{BlockSelectionState, Route};
use crate::network::radarr_network::RadarrEvent; use crate::network::radarr_network::RadarrEvent;
mod collection_details_handler; mod collection_details_handler;
@@ -1,6 +1,7 @@
use crate::app::App; use crate::app::App;
use crate::event::Key; use crate::event::Key;
use crate::handlers::{KeyEventHandler, handle_prompt_toggle}; use crate::handlers::{KeyEventHandler, handle_prompt_toggle};
use crate::models::Route;
use crate::models::servarr_data::modals::EditIndexerModal; use crate::models::servarr_data::modals::EditIndexerModal;
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, EDIT_INDEXER_BLOCKS}; use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, EDIT_INDEXER_BLOCKS};
use crate::models::servarr_models::EditIndexerParams; use crate::models::servarr_models::EditIndexerParams;
@@ -8,7 +9,6 @@ use crate::network::radarr_network::RadarrEvent;
use crate::{ use crate::{
handle_prompt_left_right_keys, handle_text_box_keys, handle_text_box_left_right_keys, matches_key, handle_prompt_left_right_keys, handle_text_box_keys, handle_text_box_left_right_keys, matches_key,
}; };
use crate::models::Route;
#[cfg(test)] #[cfg(test)]
#[path = "edit_indexer_handler_tests.rs"] #[path = "edit_indexer_handler_tests.rs"]
@@ -1,6 +1,7 @@
use crate::app::App; use crate::app::App;
use crate::event::Key; use crate::event::Key;
use crate::handlers::{KeyEventHandler, handle_prompt_toggle}; use crate::handlers::{KeyEventHandler, handle_prompt_toggle};
use crate::models::Route;
use crate::models::radarr_models::IndexerSettings; use crate::models::radarr_models::IndexerSettings;
use crate::models::servarr_data::radarr::radarr_data::{ use crate::models::servarr_data::radarr::radarr_data::{
ActiveRadarrBlock, INDEXER_SETTINGS_BLOCKS, ActiveRadarrBlock, INDEXER_SETTINGS_BLOCKS,
@@ -9,7 +10,6 @@ use crate::network::radarr_network::RadarrEvent;
use crate::{ use crate::{
handle_prompt_left_right_keys, handle_text_box_keys, handle_text_box_left_right_keys, matches_key, handle_prompt_left_right_keys, handle_text_box_keys, handle_text_box_left_right_keys, matches_key,
}; };
use crate::models::Route;
#[cfg(test)] #[cfg(test)]
#[path = "edit_indexer_settings_handler_tests.rs"] #[path = "edit_indexer_settings_handler_tests.rs"]
+1 -1
View File
@@ -7,11 +7,11 @@ use crate::handlers::radarr_handlers::indexers::test_all_indexers_handler::TestA
use crate::handlers::table_handler::{TableHandlingConfig, handle_table}; use crate::handlers::table_handler::{TableHandlingConfig, handle_table};
use crate::handlers::{KeyEventHandler, handle_clear_errors, handle_prompt_toggle}; use crate::handlers::{KeyEventHandler, handle_clear_errors, handle_prompt_toggle};
use crate::matches_key; use crate::matches_key;
use crate::models::{BlockSelectionState, Route};
use crate::models::servarr_data::radarr::radarr_data::{ use crate::models::servarr_data::radarr::radarr_data::{
ActiveRadarrBlock, EDIT_INDEXER_NZB_SELECTION_BLOCKS, EDIT_INDEXER_TORRENT_SELECTION_BLOCKS, ActiveRadarrBlock, EDIT_INDEXER_NZB_SELECTION_BLOCKS, EDIT_INDEXER_TORRENT_SELECTION_BLOCKS,
INDEXER_SETTINGS_SELECTION_BLOCKS, INDEXERS_BLOCKS, INDEXER_SETTINGS_SELECTION_BLOCKS, INDEXERS_BLOCKS,
}; };
use crate::models::{BlockSelectionState, Route};
use crate::network::radarr_network::RadarrEvent; use crate::network::radarr_network::RadarrEvent;
mod edit_indexer_handler; mod edit_indexer_handler;
@@ -2,8 +2,8 @@ use crate::app::App;
use crate::event::Key; use crate::event::Key;
use crate::handlers::{KeyEventHandler, handle_prompt_toggle}; use crate::handlers::{KeyEventHandler, handle_prompt_toggle};
use crate::matches_key; use crate::matches_key;
use crate::models::radarr_models::DeleteMovieParams;
use crate::models::Route; use crate::models::Route;
use crate::models::radarr_models::DeleteMovieParams;
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, DELETE_MOVIE_BLOCKS}; use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, DELETE_MOVIE_BLOCKS};
use crate::network::radarr_network::RadarrEvent; use crate::network::radarr_network::RadarrEvent;
@@ -1,10 +1,10 @@
use crate::app::App; use crate::app::App;
use crate::event::Key; use crate::event::Key;
use crate::handlers::{KeyEventHandler, handle_prompt_toggle}; use crate::handlers::{KeyEventHandler, handle_prompt_toggle};
use crate::models::{Route, Scrollable};
use crate::models::radarr_models::EditMovieParams; use crate::models::radarr_models::EditMovieParams;
use crate::models::servarr_data::radarr::modals::EditMovieModal; use crate::models::servarr_data::radarr::modals::EditMovieModal;
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, EDIT_MOVIE_BLOCKS}; use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, EDIT_MOVIE_BLOCKS};
use crate::models::{Route, Scrollable};
use crate::network::radarr_network::RadarrEvent; use crate::network::radarr_network::RadarrEvent;
use crate::{handle_text_box_keys, handle_text_box_left_right_keys, matches_key}; use crate::{handle_text_box_keys, handle_text_box_left_right_keys, matches_key};
+1 -1
View File
@@ -6,9 +6,9 @@ use crate::handlers::radarr_handlers::indexers::IndexersHandler;
use crate::handlers::radarr_handlers::library::LibraryHandler; use crate::handlers::radarr_handlers::library::LibraryHandler;
use crate::handlers::radarr_handlers::root_folders::RootFoldersHandler; use crate::handlers::radarr_handlers::root_folders::RootFoldersHandler;
use crate::handlers::radarr_handlers::system::SystemHandler; use crate::handlers::radarr_handlers::system::SystemHandler;
use crate::models::Route;
use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock; use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock;
use crate::{App, Key, matches_key}; use crate::{App, Key, matches_key};
use crate::models::Route;
mod blocklist; mod blocklist;
mod collections; mod collections;
@@ -3,9 +3,9 @@ use crate::event::Key;
use crate::handlers::radarr_handlers::handle_change_tab_left_right_keys; use crate::handlers::radarr_handlers::handle_change_tab_left_right_keys;
use crate::handlers::table_handler::{TableHandlingConfig, handle_table}; use crate::handlers::table_handler::{TableHandlingConfig, handle_table};
use crate::handlers::{KeyEventHandler, handle_clear_errors, handle_prompt_toggle}; use crate::handlers::{KeyEventHandler, handle_clear_errors, handle_prompt_toggle};
use crate::models::{HorizontallyScrollableText, Route};
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, ROOT_FOLDERS_BLOCKS}; use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, ROOT_FOLDERS_BLOCKS};
use crate::models::servarr_models::AddRootFolderBody; use crate::models::servarr_models::AddRootFolderBody;
use crate::models::{HorizontallyScrollableText, Route};
use crate::network::radarr_network::RadarrEvent; use crate::network::radarr_network::RadarrEvent;
use crate::{handle_text_box_keys, handle_text_box_left_right_keys, matches_key}; use crate::{handle_text_box_keys, handle_text_box_left_right_keys, matches_key};
+1 -1
View File
@@ -4,8 +4,8 @@ use crate::handlers::radarr_handlers::handle_change_tab_left_right_keys;
use crate::handlers::radarr_handlers::system::system_details_handler::SystemDetailsHandler; use crate::handlers::radarr_handlers::system::system_details_handler::SystemDetailsHandler;
use crate::handlers::{KeyEventHandler, handle_clear_errors}; use crate::handlers::{KeyEventHandler, handle_clear_errors};
use crate::matches_key; use crate::matches_key;
use crate::models::{Route, Scrollable};
use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock; use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock;
use crate::models::{Route, Scrollable};
mod system_details_handler; mod system_details_handler;
@@ -2,10 +2,10 @@ use crate::app::App;
use crate::event::Key; use crate::event::Key;
use crate::handlers::{KeyEventHandler, handle_prompt_toggle}; use crate::handlers::{KeyEventHandler, handle_prompt_toggle};
use crate::matches_key; use crate::matches_key;
use crate::models::{Route, Scrollable};
use crate::models::radarr_models::RadarrTaskName; use crate::models::radarr_models::RadarrTaskName;
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, SYSTEM_DETAILS_BLOCKS}; use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, SYSTEM_DETAILS_BLOCKS};
use crate::models::stateful_list::StatefulList; use crate::models::stateful_list::StatefulList;
use crate::models::{Route, Scrollable};
use crate::network::radarr_network::RadarrEvent; use crate::network::radarr_network::RadarrEvent;
#[cfg(test)] #[cfg(test)]
@@ -1,6 +1,7 @@
use crate::app::App; use crate::app::App;
use crate::event::Key; use crate::event::Key;
use crate::handlers::{KeyEventHandler, handle_prompt_toggle}; use crate::handlers::{KeyEventHandler, handle_prompt_toggle};
use crate::models::Route;
use crate::models::servarr_data::modals::EditIndexerModal; use crate::models::servarr_data::modals::EditIndexerModal;
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, EDIT_INDEXER_BLOCKS}; use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, EDIT_INDEXER_BLOCKS};
use crate::models::servarr_models::EditIndexerParams; use crate::models::servarr_models::EditIndexerParams;
@@ -8,7 +9,6 @@ use crate::network::sonarr_network::SonarrEvent;
use crate::{ use crate::{
handle_prompt_left_right_keys, handle_text_box_keys, handle_text_box_left_right_keys, matches_key, handle_prompt_left_right_keys, handle_text_box_keys, handle_text_box_left_right_keys, matches_key,
}; };
use crate::models::Route;
#[cfg(test)] #[cfg(test)]
#[path = "edit_indexer_handler_tests.rs"] #[path = "edit_indexer_handler_tests.rs"]
@@ -1,13 +1,13 @@
use crate::app::App; use crate::app::App;
use crate::event::Key; use crate::event::Key;
use crate::handlers::{KeyEventHandler, handle_prompt_toggle}; use crate::handlers::{KeyEventHandler, handle_prompt_toggle};
use crate::models::Route;
use crate::models::servarr_data::sonarr::sonarr_data::{ use crate::models::servarr_data::sonarr::sonarr_data::{
ActiveSonarrBlock, INDEXER_SETTINGS_BLOCKS, ActiveSonarrBlock, INDEXER_SETTINGS_BLOCKS,
}; };
use crate::models::sonarr_models::IndexerSettings; use crate::models::sonarr_models::IndexerSettings;
use crate::network::sonarr_network::SonarrEvent; use crate::network::sonarr_network::SonarrEvent;
use crate::{handle_prompt_left_right_keys, matches_key}; use crate::{handle_prompt_left_right_keys, matches_key};
use crate::models::Route;
#[cfg(test)] #[cfg(test)]
#[path = "edit_indexer_settings_handler_tests.rs"] #[path = "edit_indexer_settings_handler_tests.rs"]
+1 -1
View File
@@ -7,11 +7,11 @@ use crate::handlers::sonarr_handlers::indexers::test_all_indexers_handler::TestA
use crate::handlers::table_handler::{TableHandlingConfig, handle_table}; use crate::handlers::table_handler::{TableHandlingConfig, handle_table};
use crate::handlers::{KeyEventHandler, handle_clear_errors, handle_prompt_toggle}; use crate::handlers::{KeyEventHandler, handle_clear_errors, handle_prompt_toggle};
use crate::matches_key; use crate::matches_key;
use crate::models::{BlockSelectionState, Route};
use crate::models::servarr_data::sonarr::sonarr_data::{ use crate::models::servarr_data::sonarr::sonarr_data::{
ActiveSonarrBlock, EDIT_INDEXER_NZB_SELECTION_BLOCKS, EDIT_INDEXER_TORRENT_SELECTION_BLOCKS, ActiveSonarrBlock, EDIT_INDEXER_NZB_SELECTION_BLOCKS, EDIT_INDEXER_TORRENT_SELECTION_BLOCKS,
INDEXER_SETTINGS_SELECTION_BLOCKS, INDEXERS_BLOCKS, INDEXER_SETTINGS_SELECTION_BLOCKS, INDEXERS_BLOCKS,
}; };
use crate::models::{BlockSelectionState, Route};
use crate::network::sonarr_network::SonarrEvent; use crate::network::sonarr_network::SonarrEvent;
mod edit_indexer_handler; mod edit_indexer_handler;
@@ -1,3 +1,4 @@
use crate::models::Route;
use crate::models::sonarr_models::DeleteSeriesParams; use crate::models::sonarr_models::DeleteSeriesParams;
use crate::network::sonarr_network::SonarrEvent; use crate::network::sonarr_network::SonarrEvent;
use crate::{ use crate::{
@@ -7,7 +8,6 @@ use crate::{
matches_key, matches_key,
models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, DELETE_SERIES_BLOCKS}, models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, DELETE_SERIES_BLOCKS},
}; };
use crate::models::Route;
#[cfg(test)] #[cfg(test)]
#[path = "delete_series_handler_tests.rs"] #[path = "delete_series_handler_tests.rs"]
@@ -1,10 +1,10 @@
use crate::app::App; use crate::app::App;
use crate::event::Key; use crate::event::Key;
use crate::handlers::{KeyEventHandler, handle_prompt_toggle}; use crate::handlers::{KeyEventHandler, handle_prompt_toggle};
use crate::models::{Route, Scrollable};
use crate::models::servarr_data::sonarr::modals::EditSeriesModal; use crate::models::servarr_data::sonarr::modals::EditSeriesModal;
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, EDIT_SERIES_BLOCKS}; use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, EDIT_SERIES_BLOCKS};
use crate::models::sonarr_models::EditSeriesParams; use crate::models::sonarr_models::EditSeriesParams;
use crate::models::{Route, Scrollable};
use crate::network::sonarr_network::SonarrEvent; use crate::network::sonarr_network::SonarrEvent;
use crate::{handle_text_box_keys, handle_text_box_left_right_keys, matches_key}; use crate::{handle_text_box_keys, handle_text_box_left_right_keys, matches_key};
@@ -4,6 +4,7 @@ use crate::handlers::sonarr_handlers::history::history_sorting_options;
use crate::handlers::table_handler::{TableHandlingConfig, handle_table}; use crate::handlers::table_handler::{TableHandlingConfig, handle_table};
use crate::handlers::{KeyEventHandler, handle_prompt_toggle}; use crate::handlers::{KeyEventHandler, handle_prompt_toggle};
use crate::matches_key; use crate::matches_key;
use crate::models::Route;
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, SEASON_DETAILS_BLOCKS}; use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, SEASON_DETAILS_BLOCKS};
use crate::models::servarr_models::Language; use crate::models::servarr_models::Language;
use crate::models::sonarr_models::{ use crate::models::sonarr_models::{
@@ -12,7 +13,6 @@ use crate::models::sonarr_models::{
use crate::models::stateful_table::SortOption; use crate::models::stateful_table::SortOption;
use crate::network::sonarr_network::SonarrEvent; use crate::network::sonarr_network::SonarrEvent;
use serde_json::Number; use serde_json::Number;
use crate::models::Route;
#[cfg(test)] #[cfg(test)]
#[path = "season_details_handler_tests.rs"] #[path = "season_details_handler_tests.rs"]
@@ -4,11 +4,11 @@ use crate::handlers::sonarr_handlers::history::history_sorting_options;
use crate::handlers::table_handler::{TableHandlingConfig, handle_table}; use crate::handlers::table_handler::{TableHandlingConfig, handle_table};
use crate::handlers::{KeyEventHandler, handle_prompt_toggle}; use crate::handlers::{KeyEventHandler, handle_prompt_toggle};
use crate::matches_key; use crate::matches_key;
use crate::models::{BlockSelectionState, Route};
use crate::models::servarr_data::sonarr::sonarr_data::{ use crate::models::servarr_data::sonarr::sonarr_data::{
ActiveSonarrBlock, EDIT_SERIES_SELECTION_BLOCKS, SERIES_DETAILS_BLOCKS, ActiveSonarrBlock, EDIT_SERIES_SELECTION_BLOCKS, SERIES_DETAILS_BLOCKS,
}; };
use crate::models::sonarr_models::{Season, SonarrHistoryItem}; use crate::models::sonarr_models::{Season, SonarrHistoryItem};
use crate::models::{BlockSelectionState, Route};
use crate::network::sonarr_network::SonarrEvent; use crate::network::sonarr_network::SonarrEvent;
#[cfg(test)] #[cfg(test)]
+2 -2
View File
@@ -6,11 +6,11 @@ use library::LibraryHandler;
use root_folders::RootFoldersHandler; use root_folders::RootFoldersHandler;
use system::SystemHandler; use system::SystemHandler;
use super::KeyEventHandler;
use crate::models::Route;
use crate::{ use crate::{
app::App, event::Key, matches_key, models::servarr_data::sonarr::sonarr_data::ActiveSonarrBlock, app::App, event::Key, matches_key, models::servarr_data::sonarr::sonarr_data::ActiveSonarrBlock,
}; };
use crate::models::Route;
use super::KeyEventHandler;
mod blocklist; mod blocklist;
mod downloads; mod downloads;
@@ -3,9 +3,9 @@ use crate::event::Key;
use crate::handlers::sonarr_handlers::handle_change_tab_left_right_keys; use crate::handlers::sonarr_handlers::handle_change_tab_left_right_keys;
use crate::handlers::table_handler::{TableHandlingConfig, handle_table}; use crate::handlers::table_handler::{TableHandlingConfig, handle_table};
use crate::handlers::{KeyEventHandler, handle_clear_errors, handle_prompt_toggle}; use crate::handlers::{KeyEventHandler, handle_clear_errors, handle_prompt_toggle};
use crate::models::{HorizontallyScrollableText, Route};
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, ROOT_FOLDERS_BLOCKS}; use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, ROOT_FOLDERS_BLOCKS};
use crate::models::servarr_models::AddRootFolderBody; use crate::models::servarr_models::AddRootFolderBody;
use crate::models::{HorizontallyScrollableText, Route};
use crate::network::sonarr_network::SonarrEvent; use crate::network::sonarr_network::SonarrEvent;
use crate::{handle_text_box_keys, handle_text_box_left_right_keys, matches_key}; use crate::{handle_text_box_keys, handle_text_box_left_right_keys, matches_key};
+1 -1
View File
@@ -4,8 +4,8 @@ use crate::handlers::sonarr_handlers::handle_change_tab_left_right_keys;
use crate::handlers::sonarr_handlers::system::system_details_handler::SystemDetailsHandler; use crate::handlers::sonarr_handlers::system::system_details_handler::SystemDetailsHandler;
use crate::handlers::{KeyEventHandler, handle_clear_errors}; use crate::handlers::{KeyEventHandler, handle_clear_errors};
use crate::matches_key; use crate::matches_key;
use crate::models::{Route, Scrollable};
use crate::models::servarr_data::sonarr::sonarr_data::ActiveSonarrBlock; use crate::models::servarr_data::sonarr::sonarr_data::ActiveSonarrBlock;
use crate::models::{Route, Scrollable};
mod system_details_handler; mod system_details_handler;
@@ -2,10 +2,10 @@ use crate::app::App;
use crate::event::Key; use crate::event::Key;
use crate::handlers::{KeyEventHandler, handle_prompt_toggle}; use crate::handlers::{KeyEventHandler, handle_prompt_toggle};
use crate::matches_key; use crate::matches_key;
use crate::models::{Route, Scrollable};
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, SYSTEM_DETAILS_BLOCKS}; use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, SYSTEM_DETAILS_BLOCKS};
use crate::models::sonarr_models::SonarrTaskName; use crate::models::sonarr_models::SonarrTaskName;
use crate::models::stateful_list::StatefulList; use crate::models::stateful_list::StatefulList;
use crate::models::{Route, Scrollable};
use crate::network::sonarr_network::SonarrEvent; use crate::network::sonarr_network::SonarrEvent;
#[cfg(test)] #[cfg(test)]
+1 -1
View File
@@ -9,12 +9,12 @@ mod tests {
use crate::handlers::KeyEventHandler; use crate::handlers::KeyEventHandler;
use crate::handlers::table_handler::TableHandlingConfig; use crate::handlers::table_handler::TableHandlingConfig;
use crate::handlers::table_handler::handle_table; use crate::handlers::table_handler::handle_table;
use crate::models::Route;
use crate::models::radarr_models::Movie; use crate::models::radarr_models::Movie;
use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock; use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock;
use crate::models::servarr_models::Language; use crate::models::servarr_models::Language;
use crate::models::stateful_table::SortOption; use crate::models::stateful_table::SortOption;
use rstest::rstest; use rstest::rstest;
use crate::models::Route;
struct TableHandlerUnit<'a, 'b> { struct TableHandlerUnit<'a, 'b> {
key: Key, key: Key,
+17 -13
View File
@@ -1,5 +1,6 @@
use serde_json::Number; use serde_json::Number;
use super::modals::EditArtistModal;
use crate::app::lidarr::lidarr_context_clues::ARTISTS_CONTEXT_CLUES; use crate::app::lidarr::lidarr_context_clues::ARTISTS_CONTEXT_CLUES;
use crate::models::{ use crate::models::{
BlockSelectionState, Route, TabRoute, TabState, BlockSelectionState, Route, TabRoute, TabState,
@@ -10,16 +11,17 @@ use crate::models::{
use crate::network::lidarr_network::LidarrEvent; use crate::network::lidarr_network::LidarrEvent;
use bimap::BiMap; use bimap::BiMap;
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use strum::{EnumIter}; use strum::EnumIter;
use super::modals::EditArtistModal;
#[cfg(test)] #[cfg(test)]
use { use {
strum::{Display, EnumString, IntoEnumIterator},
crate::models::lidarr_models::NewItemMonitorType, crate::models::lidarr_models::NewItemMonitorType,
crate::models::stateful_table::SortOption, crate::models::stateful_table::SortOption,
crate::network::lidarr_network::lidarr_network_test_utils::test_utils::quality_profile_map, crate::network::lidarr_network::lidarr_network_test_utils::test_utils::quality_profile_map,
crate::network::lidarr_network::lidarr_network_test_utils::test_utils::{
download_record, metadata_profile, metadata_profile_map, quality_profile, root_folder, tags_map,
},
crate::network::servarr_test_utils::diskspace, crate::network::servarr_test_utils::diskspace,
crate::network::lidarr_network::lidarr_network_test_utils::test_utils::{download_record, metadata_profile, metadata_profile_map, quality_profile, root_folder, tags_map}, strum::{Display, EnumString, IntoEnumIterator},
}; };
#[cfg(test)] #[cfg(test)]
@@ -114,9 +116,15 @@ impl LidarrData<'_> {
tags: "alex".into(), tags: "alex".into(),
..EditArtistModal::default() ..EditArtistModal::default()
}; };
edit_artist_modal.monitor_list.set_items(NewItemMonitorType::iter().collect()); edit_artist_modal
edit_artist_modal.quality_profile_list.set_items(vec![quality_profile().name]); .monitor_list
edit_artist_modal.metadata_profile_list.set_items(vec![metadata_profile().name]); .set_items(NewItemMonitorType::iter().collect());
edit_artist_modal
.quality_profile_list
.set_items(vec![quality_profile().name]);
edit_artist_modal
.metadata_profile_list
.set_items(vec![metadata_profile().name]);
let mut lidarr_data = LidarrData { let mut lidarr_data = LidarrData {
delete_artist_files: true, delete_artist_files: true,
@@ -134,12 +142,8 @@ impl LidarrData<'_> {
}]); }]);
lidarr_data.artists.search = Some("artist search".into()); lidarr_data.artists.search = Some("artist search".into());
lidarr_data.artists.filter = Some("artist filter".into()); lidarr_data.artists.filter = Some("artist filter".into());
lidarr_data lidarr_data.downloads.set_items(vec![download_record()]);
.downloads lidarr_data.root_folders.set_items(vec![root_folder()]);
.set_items(vec![download_record()]);
lidarr_data
.root_folders
.set_items(vec![root_folder()]);
lidarr_data.version = "1.0.0".to_owned(); lidarr_data.version = "1.0.0".to_owned();
lidarr_data lidarr_data
@@ -1,12 +1,15 @@
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use bimap::BiMap;
use crate::app::lidarr::lidarr_context_clues::ARTISTS_CONTEXT_CLUES; use crate::app::lidarr::lidarr_context_clues::ARTISTS_CONTEXT_CLUES;
use crate::models::servarr_data::lidarr::lidarr_data::{DELETE_ARTIST_BLOCKS, DELETE_ARTIST_SELECTION_BLOCKS, EDIT_ARTIST_BLOCKS, EDIT_ARTIST_SELECTION_BLOCKS}; use crate::models::servarr_data::lidarr::lidarr_data::{
DELETE_ARTIST_BLOCKS, DELETE_ARTIST_SELECTION_BLOCKS, EDIT_ARTIST_BLOCKS,
EDIT_ARTIST_SELECTION_BLOCKS,
};
use crate::models::{ use crate::models::{
BlockSelectionState, Route, BlockSelectionState, Route,
servarr_data::lidarr::lidarr_data::{ActiveLidarrBlock, LIBRARY_BLOCKS, LidarrData}, servarr_data::lidarr::lidarr_data::{ActiveLidarrBlock, LIBRARY_BLOCKS, LidarrData},
}; };
use bimap::BiMap;
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use pretty_assertions::{assert_eq, assert_str_eq}; use pretty_assertions::{assert_eq, assert_str_eq};
use serde_json::Number; use serde_json::Number;
@@ -52,7 +55,10 @@ mod tests {
..LidarrData::default() ..LidarrData::default()
}; };
assert_str_eq!(lidarr_data.tag_ids_to_display(&[Number::from(1), Number::from(2)]), "test 1, test 2"); assert_str_eq!(
lidarr_data.tag_ids_to_display(&[Number::from(1), Number::from(2)]),
"test 1, test 2"
);
} }
#[test] #[test]
@@ -65,9 +71,16 @@ mod tests {
quality_profile_map, quality_profile_map,
..LidarrData::default() ..LidarrData::default()
}; };
let expected_quality_profile_vec = vec!["test 1".to_owned(), "test 2".to_owned(), "test 3".to_owned()]; let expected_quality_profile_vec = vec![
"test 1".to_owned(),
"test 2".to_owned(),
"test 3".to_owned(),
];
assert_iter_eq!(lidarr_data.sorted_quality_profile_names(), expected_quality_profile_vec); assert_iter_eq!(
lidarr_data.sorted_quality_profile_names(),
expected_quality_profile_vec
);
} }
#[test] #[test]
@@ -80,9 +93,16 @@ mod tests {
metadata_profile_map, metadata_profile_map,
..LidarrData::default() ..LidarrData::default()
}; };
let expected_metadata_profile_vec = vec!["test 1".to_owned(), "test 2".to_owned(), "test 3".to_owned()]; let expected_metadata_profile_vec = vec![
"test 1".to_owned(),
"test 2".to_owned(),
"test 3".to_owned(),
];
assert_iter_eq!(lidarr_data.sorted_metadata_profile_names(), expected_metadata_profile_vec); assert_iter_eq!(
lidarr_data.sorted_metadata_profile_names(),
expected_metadata_profile_vec
);
} }
#[test] #[test]
@@ -176,13 +196,34 @@ mod tests {
fn test_edit_artist_selection_blocks_ordering() { fn test_edit_artist_selection_blocks_ordering() {
let mut edit_artist_block_iter = EDIT_ARTIST_SELECTION_BLOCKS.iter(); let mut edit_artist_block_iter = EDIT_ARTIST_SELECTION_BLOCKS.iter();
assert_eq!(edit_artist_block_iter.next().unwrap(), &[ActiveLidarrBlock::EditArtistToggleMonitored]); assert_eq!(
assert_eq!(edit_artist_block_iter.next().unwrap(), &[ActiveLidarrBlock::EditArtistSelectMonitorNewItems]); edit_artist_block_iter.next().unwrap(),
assert_eq!(edit_artist_block_iter.next().unwrap(), &[ActiveLidarrBlock::EditArtistSelectQualityProfile]); &[ActiveLidarrBlock::EditArtistToggleMonitored]
assert_eq!(edit_artist_block_iter.next().unwrap(), &[ActiveLidarrBlock::EditArtistSelectMetadataProfile]); );
assert_eq!(edit_artist_block_iter.next().unwrap(), &[ActiveLidarrBlock::EditArtistPathInput]); assert_eq!(
assert_eq!(edit_artist_block_iter.next().unwrap(), &[ActiveLidarrBlock::EditArtistTagsInput]); edit_artist_block_iter.next().unwrap(),
assert_eq!(edit_artist_block_iter.next().unwrap(), &[ActiveLidarrBlock::EditArtistConfirmPrompt]); &[ActiveLidarrBlock::EditArtistSelectMonitorNewItems]
);
assert_eq!(
edit_artist_block_iter.next().unwrap(),
&[ActiveLidarrBlock::EditArtistSelectQualityProfile]
);
assert_eq!(
edit_artist_block_iter.next().unwrap(),
&[ActiveLidarrBlock::EditArtistSelectMetadataProfile]
);
assert_eq!(
edit_artist_block_iter.next().unwrap(),
&[ActiveLidarrBlock::EditArtistPathInput]
);
assert_eq!(
edit_artist_block_iter.next().unwrap(),
&[ActiveLidarrBlock::EditArtistTagsInput]
);
assert_eq!(
edit_artist_block_iter.next().unwrap(),
&[ActiveLidarrBlock::EditArtistConfirmPrompt]
);
assert_none!(edit_artist_block_iter.next()); assert_none!(edit_artist_block_iter.next());
} }
} }
@@ -10,8 +10,14 @@ mod tests {
#[test] #[test]
fn test_edit_artist_modal_from_lidarr_data() { fn test_edit_artist_modal_from_lidarr_data() {
let mut lidarr_data = LidarrData { let mut lidarr_data = LidarrData {
quality_profile_map: BiMap::from_iter([(1i64, "HD - 1080p".to_owned()), (2i64, "Any".to_owned())]), quality_profile_map: BiMap::from_iter([
metadata_profile_map: BiMap::from_iter([(1i64, "Standard".to_owned()), (2i64, "None".to_owned())]), (1i64, "HD - 1080p".to_owned()),
(2i64, "Any".to_owned()),
]),
metadata_profile_map: BiMap::from_iter([
(1i64, "Standard".to_owned()),
(2i64, "None".to_owned()),
]),
tags_map: BiMap::from_iter([(1i64, "usenet".to_owned())]), tags_map: BiMap::from_iter([(1i64, "usenet".to_owned())]),
..LidarrData::default() ..LidarrData::default()
}; };
@@ -1,10 +1,6 @@
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
mod radarr_data_tests { mod radarr_data_tests {
use bimap::BiMap;
use chrono::{DateTime, Utc};
use pretty_assertions::{assert_eq, assert_str_eq};
use serde_json::Number;
use crate::app::context_clues::{ use crate::app::context_clues::{
BLOCKLIST_CONTEXT_CLUES, DOWNLOADS_CONTEXT_CLUES, INDEXERS_CONTEXT_CLUES, BLOCKLIST_CONTEXT_CLUES, DOWNLOADS_CONTEXT_CLUES, INDEXERS_CONTEXT_CLUES,
ROOT_FOLDERS_CONTEXT_CLUES, SYSTEM_CONTEXT_CLUES, ROOT_FOLDERS_CONTEXT_CLUES, SYSTEM_CONTEXT_CLUES,
@@ -13,6 +9,10 @@ mod tests {
COLLECTIONS_CONTEXT_CLUES, LIBRARY_CONTEXT_CLUES, MANUAL_MOVIE_SEARCH_CONTEXT_CLUES, COLLECTIONS_CONTEXT_CLUES, LIBRARY_CONTEXT_CLUES, MANUAL_MOVIE_SEARCH_CONTEXT_CLUES,
MOVIE_DETAILS_CONTEXT_CLUES, MOVIE_DETAILS_CONTEXT_CLUES,
}; };
use bimap::BiMap;
use chrono::{DateTime, Utc};
use pretty_assertions::{assert_eq, assert_str_eq};
use serde_json::Number;
use crate::models::Route; use crate::models::Route;
use crate::models::servarr_data::radarr::radarr_data::radarr_test_utils::utils; use crate::models::servarr_data::radarr::radarr_data::radarr_test_utils::utils;
@@ -73,7 +73,10 @@ mod tests {
..RadarrData::default() ..RadarrData::default()
}; };
assert_str_eq!(radarr_data.tag_ids_to_display(&[Number::from(1), Number::from(2)]), "test 1, test 2"); assert_str_eq!(
radarr_data.tag_ids_to_display(&[Number::from(1), Number::from(2)]),
"test 1, test 2"
);
} }
#[test] #[test]
@@ -86,9 +89,16 @@ mod tests {
quality_profile_map, quality_profile_map,
..RadarrData::default() ..RadarrData::default()
}; };
let expected_quality_profile_vec = vec!["test 1".to_owned(), "test 2".to_owned(), "test 3".to_owned()]; let expected_quality_profile_vec = vec![
"test 1".to_owned(),
"test 2".to_owned(),
"test 3".to_owned(),
];
assert_iter_eq!(radarr_data.sorted_quality_profile_names(), expected_quality_profile_vec); assert_iter_eq!(
radarr_data.sorted_quality_profile_names(),
expected_quality_profile_vec
);
} }
#[test] #[test]
@@ -1,10 +1,6 @@
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
mod sonarr_data_tests { mod sonarr_data_tests {
use bimap::BiMap;
use chrono::{DateTime, Utc};
use pretty_assertions::{assert_eq, assert_str_eq};
use serde_json::Number;
use crate::app::sonarr::sonarr_context_clues::SERIES_HISTORY_CONTEXT_CLUES; use crate::app::sonarr::sonarr_context_clues::SERIES_HISTORY_CONTEXT_CLUES;
use crate::models::sonarr_models::{Season, SonarrHistoryItem}; use crate::models::sonarr_models::{Season, SonarrHistoryItem};
use crate::models::stateful_table::StatefulTable; use crate::models::stateful_table::StatefulTable;
@@ -23,6 +19,10 @@ mod tests {
servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, SonarrData}, servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, SonarrData},
}, },
}; };
use bimap::BiMap;
use chrono::{DateTime, Utc};
use pretty_assertions::{assert_eq, assert_str_eq};
use serde_json::Number;
#[test] #[test]
fn test_from_active_sonarr_block_to_route() { fn test_from_active_sonarr_block_to_route() {
@@ -89,7 +89,10 @@ mod tests {
..SonarrData::default() ..SonarrData::default()
}; };
assert_str_eq!(sonarr_data.tag_ids_to_display(&[Number::from(1), Number::from(2)]), "test 1, test 2"); assert_str_eq!(
sonarr_data.tag_ids_to_display(&[Number::from(1), Number::from(2)]),
"test 1, test 2"
);
} }
#[test] #[test]
@@ -102,9 +105,16 @@ mod tests {
quality_profile_map, quality_profile_map,
..SonarrData::default() ..SonarrData::default()
}; };
let expected_quality_profile_vec = vec!["test 1".to_owned(), "test 2".to_owned(), "test 3".to_owned()]; let expected_quality_profile_vec = vec![
"test 1".to_owned(),
"test 2".to_owned(),
"test 3".to_owned(),
];
assert_iter_eq!(sonarr_data.sorted_quality_profile_names(), expected_quality_profile_vec); assert_iter_eq!(
sonarr_data.sorted_quality_profile_names(),
expected_quality_profile_vec
);
} }
#[test] #[test]
@@ -117,9 +127,16 @@ mod tests {
language_profiles_map, language_profiles_map,
..SonarrData::default() ..SonarrData::default()
}; };
let expected_language_profiles_vec = vec!["test 1".to_owned(), "test 2".to_owned(), "test 3".to_owned()]; let expected_language_profiles_vec = vec![
"test 1".to_owned(),
"test 2".to_owned(),
"test 3".to_owned(),
];
assert_iter_eq!(sonarr_data.sorted_language_profile_names(), expected_language_profiles_vec); assert_iter_eq!(
sonarr_data.sorted_language_profile_names(),
expected_language_profiles_vec
);
} }
#[test] #[test]
@@ -1,11 +1,16 @@
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::models::lidarr_models::{Artist, DeleteArtistParams, LidarrSerdeable}; use crate::models::lidarr_models::{
Artist, DeleteArtistParams, EditArtistParams, LidarrSerdeable, NewItemMonitorType,
};
use crate::network::NetworkResource;
use crate::network::lidarr_network::LidarrEvent; use crate::network::lidarr_network::LidarrEvent;
use crate::network::network_tests::test_utils::{MockServarrApi, test_network}; use crate::network::network_tests::test_utils::{MockServarrApi, test_network};
use bimap::BiMap;
use mockito::Matcher; use mockito::Matcher;
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
use serde_json::json; use serde_json::{Value, json};
use crate::network::lidarr_network::lidarr_network_test_utils::test_utils::ARTIST_JSON;
#[tokio::test] #[tokio::test]
async fn test_handle_list_artists_event() { async fn test_handle_list_artists_event() {
@@ -70,36 +75,9 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn test_handle_get_artist_details_event() { async fn test_handle_get_artist_details_event() {
let artist_json = json!({ let expected_artist: Artist = serde_json::from_str(ARTIST_JSON).unwrap();
"id": 1,
"artistName": "Test Artist",
"foreignArtistId": "test-foreign-id",
"status": "continuing",
"overview": "some interesting description of the artist",
"artistType": "Person",
"disambiguation": "American pianist",
"path": "/music/test-artist",
"members": [{"name": "alex", "instrument": "piano"}],
"qualityProfileId": 1,
"metadataProfileId": 1,
"monitored": true,
"monitorNewItems": "all",
"genres": ["soundtrack"],
"tags": [1],
"added": "2023-01-01T00:00:00Z",
"ratings": { "votes": 15, "value": 8.4 },
"statistics": {
"albumCount": 1,
"trackFileCount": 15,
"trackCount": 15,
"totalTrackCount": 15,
"sizeOnDisk": 12345,
"percentOfTracks": 99.9
}
});
let response: Artist = serde_json::from_value(artist_json.clone()).unwrap();
let (mock, app, _server) = MockServarrApi::get() let (mock, app, _server) = MockServarrApi::get()
.returns(artist_json) .returns(serde_json::from_str(ARTIST_JSON).unwrap())
.path("/1") .path("/1")
.build_for(LidarrEvent::GetArtistDetails(1)) .build_for(LidarrEvent::GetArtistDetails(1))
.await; .await;
@@ -116,7 +94,7 @@ mod tests {
panic!("Expected Artist"); panic!("Expected Artist");
}; };
assert_eq!(artist, response); assert_eq!(artist, expected_artist);
} }
#[tokio::test] #[tokio::test]
@@ -184,4 +162,198 @@ mod tests {
mock.assert_async().await; mock.assert_async().await;
} }
#[tokio::test]
async fn test_handle_edit_artist_event() {
let mut expected_body: Value = serde_json::from_str(ARTIST_JSON).unwrap();
*expected_body.get_mut("monitored").unwrap() = json!(false);
*expected_body.get_mut("monitorNewItems").unwrap() = json!("none");
*expected_body.get_mut("qualityProfileId").unwrap() = json!(1111);
*expected_body.get_mut("metadataProfileId").unwrap() = json!(2222);
*expected_body.get_mut("path").unwrap() = json!("/nfs/Test Path");
*expected_body.get_mut("tags").unwrap() = json!([1, 2]);
let edit_artist_params = EditArtistParams {
artist_id: 1,
monitored: Some(false),
monitor_new_items: Some(NewItemMonitorType::None),
quality_profile_id: Some(1111),
metadata_profile_id: Some(2222),
root_folder_path: Some("/nfs/Test Path".to_owned()),
tag_input_string: Some("usenet, testing".to_owned()),
..EditArtistParams::default()
};
let (async_details_server, app, mut server) = MockServarrApi::get()
.returns(serde_json::from_str(ARTIST_JSON).unwrap())
.path("/1")
.build_for(LidarrEvent::GetArtistDetails(1))
.await;
let async_edit_server = server
.mock(
"PUT",
format!(
"/api/v1{}/1",
LidarrEvent::EditArtist(edit_artist_params.clone()).resource()
)
.as_str(),
)
.with_status(202)
.match_header("X-Api-Key", "test1234")
.match_body(Matcher::Json(expected_body))
.create_async()
.await;
app.lock().await.data.lidarr_data.tags_map =
BiMap::from_iter([(1, "usenet".to_owned()), (2, "testing".to_owned())]);
app.lock().await.server_tabs.set_index(2);
let mut network = test_network(&app);
assert!(
network
.handle_lidarr_event(LidarrEvent::EditArtist(edit_artist_params))
.await
.is_ok()
);
async_details_server.assert_async().await;
async_edit_server.assert_async().await;
}
#[tokio::test]
async fn test_handle_edit_artist_event_does_not_overwrite_tag_ids_vec_when_tag_input_string_is_none()
{
let mut expected_body: Value = serde_json::from_str(ARTIST_JSON).unwrap();
*expected_body.get_mut("monitored").unwrap() = json!(false);
*expected_body.get_mut("monitorNewItems").unwrap() = json!("none");
*expected_body.get_mut("qualityProfileId").unwrap() = json!(1111);
*expected_body.get_mut("metadataProfileId").unwrap() = json!(2222);
*expected_body.get_mut("path").unwrap() = json!("/nfs/Test Path");
*expected_body.get_mut("tags").unwrap() = json!([1, 2]);
let edit_artist_params = EditArtistParams {
artist_id: 1,
monitored: Some(false),
monitor_new_items: Some(NewItemMonitorType::None),
quality_profile_id: Some(1111),
metadata_profile_id: Some(2222),
root_folder_path: Some("/nfs/Test Path".to_owned()),
tags: Some(vec![1, 2]),
..EditArtistParams::default()
};
let (async_details_server, app, mut server) = MockServarrApi::get()
.returns(serde_json::from_str(ARTIST_JSON).unwrap())
.path("/1")
.build_for(LidarrEvent::GetArtistDetails(1))
.await;
let async_edit_server = server
.mock(
"PUT",
format!(
"/api/v1{}/1",
LidarrEvent::EditArtist(edit_artist_params.clone()).resource()
)
.as_str(),
)
.with_status(202)
.match_header("X-Api-Key", "test1234")
.match_body(Matcher::Json(expected_body))
.create_async()
.await;
app.lock().await.data.lidarr_data.tags_map =
BiMap::from_iter([(1, "usenet".to_owned()), (2, "testing".to_owned())]);
app.lock().await.server_tabs.set_index(2);
let mut network = test_network(&app);
assert!(
network
.handle_lidarr_event(LidarrEvent::EditArtist(edit_artist_params))
.await
.is_ok()
);
async_details_server.assert_async().await;
async_edit_server.assert_async().await;
}
#[tokio::test]
async fn test_handle_edit_artist_event_defaults_to_previous_values() {
let edit_artist_params = EditArtistParams {
artist_id: 1,
..EditArtistParams::default()
};
let expected_body: Value = serde_json::from_str(ARTIST_JSON).unwrap();
let (async_details_server, app, mut server) = MockServarrApi::get()
.returns(serde_json::from_str(ARTIST_JSON).unwrap())
.path("/1")
.build_for(LidarrEvent::GetArtistDetails(1))
.await;
let async_edit_server = server
.mock(
"PUT",
format!(
"/api/v1{}/1",
LidarrEvent::EditArtist(edit_artist_params.clone()).resource()
)
.as_str(),
)
.with_status(202)
.match_header("X-Api-Key", "test1234")
.match_body(Matcher::Json(expected_body))
.create_async()
.await;
app.lock().await.server_tabs.set_index(2);
let mut network = test_network(&app);
assert!(
network
.handle_lidarr_event(LidarrEvent::EditArtist(edit_artist_params))
.await
.is_ok()
);
async_details_server.assert_async().await;
async_edit_server.assert_async().await;
}
#[tokio::test]
async fn test_handle_edit_artist_event_returns_empty_tags_vec_when_clear_tags_is_true() {
let mut expected_body: Value = serde_json::from_str(ARTIST_JSON).unwrap();
*expected_body.get_mut("tags").unwrap() = json!([]);
let (async_details_server, app, mut server) = MockServarrApi::get()
.returns(serde_json::from_str(ARTIST_JSON).unwrap())
.path("/1")
.build_for(LidarrEvent::GetArtistDetails(1))
.await;
let edit_artist_params = EditArtistParams {
artist_id: 1,
clear_tags: true,
..EditArtistParams::default()
};
let async_edit_server = server
.mock(
"PUT",
format!(
"/api/v1{}/1",
LidarrEvent::EditArtist(edit_artist_params.clone()).resource()
)
.as_str(),
)
.with_status(202)
.match_header("X-Api-Key", "test1234")
.match_body(Matcher::Json(expected_body))
.create_async()
.await;
app.lock().await.server_tabs.set_index(2);
let mut network = test_network(&app);
assert!(
network
.handle_lidarr_event(LidarrEvent::EditArtist(edit_artist_params))
.await
.is_ok()
);
async_details_server.assert_async().await;
async_edit_server.assert_async().await;
}
} }
@@ -1,24 +1,55 @@
#[cfg(test)] #[cfg(test)]
#[allow(dead_code)] // TODO: maybe remove? #[allow(dead_code)] // TODO: maybe remove?
pub mod test_utils { pub mod test_utils {
use crate::models::lidarr_models::{Artist, ArtistStatistics, ArtistStatus, DownloadRecord, DownloadStatus, DownloadsResponse, Member, MetadataProfile, NewItemMonitorType, Ratings, SystemStatus};
use crate::models::servarr_models::{QualityProfile, RootFolder, Tag};
use crate::models::HorizontallyScrollableText; use crate::models::HorizontallyScrollableText;
use crate::models::lidarr_models::{
Artist, ArtistStatistics, ArtistStatus, DownloadRecord, DownloadStatus, DownloadsResponse,
EditArtistParams, Member, MetadataProfile, NewItemMonitorType, Ratings, SystemStatus,
};
use crate::models::servarr_models::{QualityProfile, RootFolder, Tag};
use bimap::BiMap; use bimap::BiMap;
use chrono::DateTime; use chrono::DateTime;
use serde_json::Number; use serde_json::Number;
pub const ARTIST_JSON: &str = r#"{
"id": 1,
"artistName": "Test Artist",
"foreignArtistId": "test-foreign-id",
"status": "continuing",
"overview": "some interesting description of the artist",
"artistType": "Person",
"disambiguation": "American pianist",
"path": "/music/test-artist",
"members": [{"name": "alex", "instrument": "piano"}],
"qualityProfileId": 1,
"metadataProfileId": 1,
"monitored": true,
"monitorNewItems": "all",
"genres": ["soundtrack"],
"tags": [1],
"added": "2023-01-01T00:00:00Z",
"ratings": { "votes": 15, "value": 8.4 },
"statistics": {
"albumCount": 1,
"trackFileCount": 15,
"trackCount": 15,
"totalTrackCount": 15,
"sizeOnDisk": 12345,
"percentOfTracks": 99.9
}
}"#;
pub fn member() -> Member { pub fn member() -> Member {
Member { Member {
name: Some("alex".to_owned()), name: Some("alex".to_owned()),
instrument: Some("piano".to_owned()) instrument: Some("piano".to_owned()),
} }
} }
pub fn ratings() -> Ratings { pub fn ratings() -> Ratings {
Ratings { Ratings {
votes: 15, votes: 15,
value: 8.4 value: 8.4,
} }
} }
@@ -29,7 +60,7 @@ pub mod test_utils {
track_count: 15, track_count: 15,
total_track_count: 15, total_track_count: 15,
size_on_disk: 12345, size_on_disk: 12345,
percent_of_tracks: 99.9 percent_of_tracks: 99.9,
} }
} }
@@ -52,14 +83,14 @@ pub mod test_utils {
tags: vec![Number::from(tag().id)], tags: vec![Number::from(tag().id)],
added: DateTime::from(DateTime::parse_from_rfc3339("2023-01-01T00:00:00Z").unwrap()), added: DateTime::from(DateTime::parse_from_rfc3339("2023-01-01T00:00:00Z").unwrap()),
ratings: Some(ratings()), ratings: Some(ratings()),
statistics: Some(artist_statistics()) statistics: Some(artist_statistics()),
} }
} }
pub fn quality_profile() -> QualityProfile { pub fn quality_profile() -> QualityProfile {
QualityProfile { QualityProfile {
id: 1, id: 1,
name: "Lossless".to_owned() name: "Lossless".to_owned(),
} }
} }
@@ -71,7 +102,7 @@ pub mod test_utils {
pub fn metadata_profile() -> MetadataProfile { pub fn metadata_profile() -> MetadataProfile {
MetadataProfile { MetadataProfile {
id: 1, id: 1,
name: "Standard".to_owned() name: "Standard".to_owned(),
} }
} }
@@ -83,7 +114,7 @@ pub mod test_utils {
pub fn tag() -> Tag { pub fn tag() -> Tag {
Tag { Tag {
id: 1, id: 1,
label: "alex".to_owned() label: "alex".to_owned(),
} }
} }
@@ -103,13 +134,13 @@ pub mod test_utils {
sizeleft: 1771674009f64, sizeleft: 1771674009f64,
output_path: Some(HorizontallyScrollableText::from("/nfs/music/alex/album")), output_path: Some(HorizontallyScrollableText::from("/nfs/music/alex/album")),
indexer: "kickass torrents".to_owned(), indexer: "kickass torrents".to_owned(),
download_client: Some("transmission".to_owned()) download_client: Some("transmission".to_owned()),
} }
} }
pub fn downloads_response() -> DownloadsResponse { pub fn downloads_response() -> DownloadsResponse {
DownloadsResponse { DownloadsResponse {
records: vec![download_record()] records: vec![download_record()],
} }
} }
@@ -129,4 +160,18 @@ pub mod test_utils {
unmapped_folders: None, unmapped_folders: None,
} }
} }
pub fn edit_artist_params() -> EditArtistParams {
EditArtistParams {
artist_id: artist().id,
monitored: Some(true),
monitor_new_items: Some(NewItemMonitorType::All),
quality_profile_id: Some(quality_profile().id),
metadata_profile_id: Some(metadata_profile().id),
root_folder_path: Some("/nfs/music/test-artist".to_owned()),
tags: Some(vec![tag().id]),
tag_input_string: Some("alex".to_owned()),
clear_tags: false,
}
}
} }
@@ -1,12 +1,17 @@
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::sync::Arc;
use crate::models::lidarr_models::{LidarrSerdeable, MetadataProfile}; use crate::models::lidarr_models::{LidarrSerdeable, MetadataProfile};
use crate::models::servarr_models::{QualityProfile, Tag}; use crate::models::servarr_models::{QualityProfile, Tag};
use crate::network::network_tests::test_utils::{MockServarrApi, test_network}; use crate::network::network_tests::test_utils::{MockServarrApi, test_network};
use crate::network::{NetworkEvent, NetworkResource, lidarr_network::LidarrEvent}; use crate::network::{NetworkEvent, NetworkResource, lidarr_network::LidarrEvent};
use bimap::BiMap;
use pretty_assertions::{assert_eq, assert_str_eq}; use pretty_assertions::{assert_eq, assert_str_eq};
use rstest::rstest; use rstest::rstest;
use serde_json::json; use serde_json::json;
use tokio::sync::Mutex;
use crate::app::App;
use crate::models::servarr_data::lidarr::modals::EditArtistModal;
#[rstest] #[rstest]
fn test_resource_artist( fn test_resource_artist(
@@ -170,4 +175,91 @@ mod tests {
Some(&"usenet".to_owned()) Some(&"usenet".to_owned())
); );
} }
#[tokio::test]
async fn test_handle_add_lidarr_tag_event() {
let tag_json = json!({
"id": 1,
"label": "usenet"
});
let response: Tag = serde_json::from_value(tag_json.clone()).unwrap();
let (mock, app, _server) = MockServarrApi::post()
.with_request_body(json!({ "label": "usenet" }))
.returns(tag_json)
.build_for(LidarrEvent::AddTag("usenet".to_owned()))
.await;
app.lock().await.server_tabs.set_index(2);
let mut network = test_network(&app);
let result = network
.handle_lidarr_event(LidarrEvent::AddTag("usenet".to_owned()))
.await;
mock.assert_async().await;
let LidarrSerdeable::Tag(tag) = result.unwrap() else {
panic!("Expected Tag");
};
assert_eq!(tag, response);
assert_eq!(
app.lock().await.data.lidarr_data.tags_map.get_by_left(&1),
Some(&"usenet".to_owned())
);
}
#[tokio::test]
async fn test_extract_and_add_lidarr_tag_ids_vec() {
let app_arc = Arc::new(Mutex::new(App::test_default()));
let tags = " test,HI ,, usenet ";
{
let mut app = app_arc.lock().await;
app.data.lidarr_data.tags_map = BiMap::from_iter([
(1, "usenet".to_owned()),
(2, "test".to_owned()),
(3, "hi".to_owned()),
]);
}
app_arc.lock().await.server_tabs.set_index(2);
let mut network = test_network(&app_arc);
assert_eq!(
network.extract_and_add_lidarr_tag_ids_vec(tags).await,
vec![2, 3, 1]
);
}
#[tokio::test]
async fn test_extract_and_add_lidarr_tag_ids_vec_add_missing_tags_first() {
let (mock, app, _server) = MockServarrApi::post()
.with_request_body(json!({ "label": "TESTING" }))
.returns(json!({ "id": 3, "label": "testing" }))
.build_for(LidarrEvent::GetTags)
.await;
let tags = "usenet, test, TESTING";
{
let mut app_guard = app.lock().await;
app_guard.data.lidarr_data.edit_artist_modal = Some(EditArtistModal {
tags: tags.into(),
..EditArtistModal::default()
});
app_guard.data.lidarr_data.tags_map =
BiMap::from_iter([(1, "usenet".to_owned()), (2, "test".to_owned())]);
}
app.lock().await.server_tabs.set_index(2);
let mut network = test_network(&app);
let tag_ids_vec = network.extract_and_add_lidarr_tag_ids_vec(tags).await;
mock.assert_async().await;
assert_eq!(tag_ids_vec, vec![1, 2, 3]);
assert_eq!(
app.lock().await.data.lidarr_data.tags_map,
BiMap::from_iter([
(1, "usenet".to_owned()),
(2, "test".to_owned()),
(3, "testing".to_owned())
])
);
}
} }
+1 -1
View File
@@ -12,7 +12,7 @@ use crate::models::servarr_data::lidarr::modals::EditArtistModal;
use crate::render_selectable_input_box; use crate::render_selectable_input_box;
use crate::ui::styles::ManagarrStyle; use crate::ui::styles::ManagarrStyle;
use crate::ui::utils::{title_block_centered}; use crate::ui::utils::title_block_centered;
use crate::ui::widgets::button::Button; use crate::ui::widgets::button::Button;
use crate::ui::widgets::checkbox::Checkbox; use crate::ui::widgets::checkbox::Checkbox;
use crate::ui::widgets::input_box::InputBox; use crate::ui::widgets::input_box::InputBox;
@@ -1,22 +1,49 @@
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use pretty_assertions::assert_eq;
use strum::IntoEnumIterator; use strum::IntoEnumIterator;
use crate::models::Route; use crate::app::App;
use crate::models::servarr_data::lidarr::lidarr_data::{ActiveLidarrBlock, EDIT_ARTIST_BLOCKS}; use crate::models::BlockSelectionState;
use crate::models::servarr_data::lidarr::lidarr_data::{
ActiveLidarrBlock, EDIT_ARTIST_BLOCKS, EDIT_ARTIST_SELECTION_BLOCKS,
};
use crate::ui::DrawUi; use crate::ui::DrawUi;
use crate::ui::lidarr_ui::library::edit_artist_ui::EditArtistUi; use crate::ui::lidarr_ui::library::edit_artist_ui::EditArtistUi;
use crate::ui::ui_test_utils::test_utils::render_to_string_with_app;
#[test] #[test]
fn test_edit_artist_ui_accepts() { fn test_edit_artist_ui_accepts() {
let mut edit_artist_ui_blocks = Vec::new(); ActiveLidarrBlock::iter().for_each(|active_lidarr_block| {
for block in ActiveLidarrBlock::iter() { if EDIT_ARTIST_BLOCKS.contains(&active_lidarr_block) {
if EditArtistUi::accepts(Route::Lidarr(block, None)) { assert!(EditArtistUi::accepts(active_lidarr_block.into()));
edit_artist_ui_blocks.push(block); } else {
assert!(!EditArtistUi::accepts(active_lidarr_block.into()));
} }
});
} }
assert_eq!(edit_artist_ui_blocks, EDIT_ARTIST_BLOCKS.to_vec()); mod snapshot_tests {
use crate::ui::ui_test_utils::test_utils::TerminalSize;
use rstest::rstest;
use super::*;
#[rstest]
#[case(ActiveLidarrBlock::EditArtistPrompt)]
#[case(ActiveLidarrBlock::EditArtistConfirmPrompt)]
#[case(ActiveLidarrBlock::EditArtistSelectMetadataProfile)]
#[case(ActiveLidarrBlock::EditArtistSelectMonitorNewItems)]
#[case(ActiveLidarrBlock::EditArtistSelectQualityProfile)]
fn test_edit_artist_ui_renders(#[case] active_lidarr_block: ActiveLidarrBlock) {
let mut app = App::test_default_fully_populated();
app.push_navigation_stack(active_lidarr_block.into());
app.data.lidarr_data.selected_block = BlockSelectionState::new(EDIT_ARTIST_SELECTION_BLOCKS);
let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| {
EditArtistUi::draw(f, app, f.area());
});
insta::assert_snapshot!(format!("edit_artist_{active_lidarr_block}"), output);
}
} }
} }
@@ -24,7 +24,7 @@ expression: output
│ │ │ │
│ │ │ │
│ ╭───╮ │ │ ╭───╮ │
│ Delete Artist Files: │ │ │ │ Delete Artist Files: │ │ │
│ ╰───╯ │ │ ╰───╯ │
│ ╭───╮ │ │ ╭───╮ │
│ Add List Exclusion: │ │ │ │ Add List Exclusion: │ │ │
@@ -0,0 +1,48 @@
---
source: src/ui/lidarr_ui/library/edit_artist_ui_tests.rs
expression: output
---
╭─────────────────────────────────────────────── Edit - ───────────────────────────────────────────────╮
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ ╭───╮ │
│ Monitored: │ ✔ │ │
│ ╰───╯ │
│ ╭─────────────────────────────────────────────────╮ │
│ Monitor New Albums: │All Albums ▼ │ │
│ ╰─────────────────────────────────────────────────╯ │
│ ╭─────────────────────────────────────────────────╮ │
│ Quality Profile: │Lossless ▼ │ │
│ ╰─────────────────────────────────────────────────╯ │
│ ╭─────────────────────────────────────────────────╮ │
│ Metadata Profile: │Standard ▼ │ │
│ ╰─────────────────────────────────────────────────╯ │
│ ╭─────────────────────────────────────────────────╮ │
│ Path: │/nfs/music │ │
│ ╰─────────────────────────────────────────────────╯ │
│ ╭─────────────────────────────────────────────────╮ │
│ Tags: │alex │ │
│ ╰─────────────────────────────────────────────────╯ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│╭───────────────────────────────────────────────────╮╭──────────────────────────────────────────────────╮│
││ Save ││ Cancel ││
│╰───────────────────────────────────────────────────╯╰──────────────────────────────────────────────────╯│
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯
@@ -0,0 +1,48 @@
---
source: src/ui/lidarr_ui/library/edit_artist_ui_tests.rs
expression: output
---
╭─────────────────────────────────────────────── Edit - ───────────────────────────────────────────────╮
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ ╭───╮ │
│ Monitored: │ ✔ │ │
│ ╰───╯ │
│ ╭─────────────────────────────────────────────────╮ │
│ Monitor New Albums: │All Albums ▼ │ │
│ ╰─────────────────────────────────────────────────╯ │
│ ╭─────────────────────────────────────────────────╮ │
│ Quality Profile: │Lossless ▼ │ │
│ ╰─────────────────────────────────────────────────╯ │
│ ╭─────────────────────────────────────────────────╮ │
│ Metadata Profile: │Standard ▼ │ │
│ ╰─────────────────────────────────────────────────╯ │
│ ╭─────────────────────────────────────────────────╮ │
│ Path: │/nfs/music │ │
│ ╰─────────────────────────────────────────────────╯ │
│ ╭─────────────────────────────────────────────────╮ │
│ Tags: │alex │ │
│ ╰─────────────────────────────────────────────────╯ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│╭───────────────────────────────────────────────────╮╭──────────────────────────────────────────────────╮│
││ Save ││ Cancel ││
│╰───────────────────────────────────────────────────╯╰──────────────────────────────────────────────────╯│
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯
@@ -0,0 +1,48 @@
---
source: src/ui/lidarr_ui/library/edit_artist_ui_tests.rs
expression: output
---
╭─────────────────────────────────────────────── Edit - ───────────────────────────────────────────────╮
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ ╭───╮ │
│ Monitored: │ ✔ │ │
│ ╰───╯ │
│ ╭───────────────────────────────╮──────────────────────────────╮ │
│ Monitor│Standard │ ▼ │ │
│ │ │──────────────────────────────╯ │
│ │ │──────────────────────────────╮ │
│ Qual│ │ ▼ │ │
│ │ │──────────────────────────────╯ │
│ │ │──────────────────────────────╮ │
│ Metad│ │ ▼ │ │
│ │ │──────────────────────────────╯ │
│ │ │──────────────────────────────╮ │
│ │ │ │ │
│ │ │──────────────────────────────╯ │
│ │ │──────────────────────────────╮ │
│ │ │ │ │
│ ╰───────────────────────────────╯──────────────────────────────╯ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│╭───────────────────────────────────────────────────╮╭──────────────────────────────────────────────────╮│
││ Save ││ Cancel ││
│╰───────────────────────────────────────────────────╯╰──────────────────────────────────────────────────╯│
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯
@@ -0,0 +1,48 @@
---
source: src/ui/lidarr_ui/library/edit_artist_ui_tests.rs
expression: output
---
╭─────────────────────────────────────────────── Edit - ───────────────────────────────────────────────╮
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ ╭───╮ │
│ Monitored: │ ✔ │ │
│ ╰───╯ │
│ ╭───────────────────────────────╮──────────────────────────────╮ │
│ Monitor│All Albums │ ▼ │ │
│ │No New Albums │──────────────────────────────╯ │
│ │New Albums │──────────────────────────────╮ │
│ Qual│ │ ▼ │ │
│ │ │──────────────────────────────╯ │
│ │ │──────────────────────────────╮ │
│ Metad│ │ ▼ │ │
│ │ │──────────────────────────────╯ │
│ │ │──────────────────────────────╮ │
│ │ │ │ │
│ │ │──────────────────────────────╯ │
│ │ │──────────────────────────────╮ │
│ │ │ │ │
│ ╰───────────────────────────────╯──────────────────────────────╯ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│╭───────────────────────────────────────────────────╮╭──────────────────────────────────────────────────╮│
││ Save ││ Cancel ││
│╰───────────────────────────────────────────────────╯╰──────────────────────────────────────────────────╯│
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯
@@ -0,0 +1,48 @@
---
source: src/ui/lidarr_ui/library/edit_artist_ui_tests.rs
expression: output
---
╭─────────────────────────────────────────────── Edit - ───────────────────────────────────────────────╮
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ ╭───╮ │
│ Monitored: │ ✔ │ │
│ ╰───╯ │
│ ╭───────────────────────────────╮──────────────────────────────╮ │
│ Monitor│Lossless │ ▼ │ │
│ │ │──────────────────────────────╯ │
│ │ │──────────────────────────────╮ │
│ Qual│ │ ▼ │ │
│ │ │──────────────────────────────╯ │
│ │ │──────────────────────────────╮ │
│ Metad│ │ ▼ │ │
│ │ │──────────────────────────────╯ │
│ │ │──────────────────────────────╮ │
│ │ │ │ │
│ │ │──────────────────────────────╯ │
│ │ │──────────────────────────────╮ │
│ │ │ │ │
│ ╰───────────────────────────────╯──────────────────────────────╯ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│╭───────────────────────────────────────────────────╮╭──────────────────────────────────────────────────╮│
││ Save ││ Cancel ││
│╰───────────────────────────────────────────────────╯╰──────────────────────────────────────────────────╯│
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯
@@ -24,7 +24,7 @@ expression: output
│ │ │ │
│ │ │ │
│ ╭───╮ │ │ ╭───╮ │
│ Delete Artist Files: │ │ │ │ Delete Artist Files: │ │ │
│ ╰───╯ │ │ ╰───╯ │
│ ╭───╮ │ │ ╭───╮ │
│ Add List Exclusion: │ │ │ │ Add List Exclusion: │ │ │