feat: Full Lidarr system support for both the CLI and TUI
This commit is contained in:
@@ -124,3 +124,8 @@ pub static SYSTEM_CONTEXT_CLUES: [ContextClue; 5] = [
|
|||||||
DEFAULT_KEYBINDINGS.refresh.desc,
|
DEFAULT_KEYBINDINGS.refresh.desc,
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
pub static SYSTEM_TASKS_CONTEXT_CLUES: [ContextClue; 2] = [
|
||||||
|
(DEFAULT_KEYBINDINGS.submit, "start task"),
|
||||||
|
(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc),
|
||||||
|
];
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ mod test {
|
|||||||
BARE_POPUP_CONTEXT_CLUES, BLOCKLIST_CONTEXT_CLUES, CONFIRMATION_PROMPT_CONTEXT_CLUES,
|
BARE_POPUP_CONTEXT_CLUES, BLOCKLIST_CONTEXT_CLUES, CONFIRMATION_PROMPT_CONTEXT_CLUES,
|
||||||
ContextClueProvider, DOWNLOADS_CONTEXT_CLUES, HISTORY_CONTEXT_CLUES, INDEXERS_CONTEXT_CLUES,
|
ContextClueProvider, DOWNLOADS_CONTEXT_CLUES, HISTORY_CONTEXT_CLUES, INDEXERS_CONTEXT_CLUES,
|
||||||
ROOT_FOLDERS_CONTEXT_CLUES, SERVARR_CONTEXT_CLUES, SYSTEM_CONTEXT_CLUES,
|
ROOT_FOLDERS_CONTEXT_CLUES, SERVARR_CONTEXT_CLUES, SYSTEM_CONTEXT_CLUES,
|
||||||
ServarrContextClueProvider,
|
SYSTEM_TASKS_CONTEXT_CLUES, ServarrContextClueProvider,
|
||||||
};
|
};
|
||||||
use crate::app::{App, key_binding::DEFAULT_KEYBINDINGS};
|
use crate::app::{App, key_binding::DEFAULT_KEYBINDINGS};
|
||||||
use crate::models::servarr_data::ActiveKeybindingBlock;
|
use crate::models::servarr_data::ActiveKeybindingBlock;
|
||||||
@@ -268,6 +268,21 @@ mod test {
|
|||||||
assert_none!(system_context_clues_iter.next());
|
assert_none!(system_context_clues_iter.next());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_system_tasks_context_clues() {
|
||||||
|
let mut system_tasks_context_clues_iter = SYSTEM_TASKS_CONTEXT_CLUES.iter();
|
||||||
|
|
||||||
|
assert_some_eq_x!(
|
||||||
|
system_tasks_context_clues_iter.next(),
|
||||||
|
&(DEFAULT_KEYBINDINGS.submit, "start task")
|
||||||
|
);
|
||||||
|
assert_some_eq_x!(
|
||||||
|
system_tasks_context_clues_iter.next(),
|
||||||
|
&(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc)
|
||||||
|
);
|
||||||
|
assert_none!(system_tasks_context_clues_iter.next());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_servarr_context_clue_provider_delegates_to_radarr_provider() {
|
fn test_servarr_context_clue_provider_delegates_to_radarr_provider() {
|
||||||
let mut app = App::test_default();
|
let mut app = App::test_default();
|
||||||
@@ -275,10 +290,7 @@ mod test {
|
|||||||
|
|
||||||
let context_clues = ServarrContextClueProvider::get_context_clues(&mut app);
|
let context_clues = ServarrContextClueProvider::get_context_clues(&mut app);
|
||||||
|
|
||||||
assert_some_eq_x!(
|
assert_some_eq_x!(context_clues, &SYSTEM_TASKS_CONTEXT_CLUES,);
|
||||||
context_clues,
|
|
||||||
&crate::app::radarr::radarr_context_clues::SYSTEM_TASKS_CONTEXT_CLUES,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -288,10 +300,7 @@ mod test {
|
|||||||
|
|
||||||
let context_clues = ServarrContextClueProvider::get_context_clues(&mut app);
|
let context_clues = ServarrContextClueProvider::get_context_clues(&mut app);
|
||||||
|
|
||||||
assert_some_eq_x!(
|
assert_some_eq_x!(context_clues, &SYSTEM_TASKS_CONTEXT_CLUES,);
|
||||||
context_clues,
|
|
||||||
&crate::app::sonarr::sonarr_context_clues::SYSTEM_TASKS_CONTEXT_CLUES,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use crate::app::App;
|
use crate::app::App;
|
||||||
use crate::app::context_clues::{
|
use crate::app::context_clues::{
|
||||||
BARE_POPUP_CONTEXT_CLUES, CONFIRMATION_PROMPT_CONTEXT_CLUES, ContextClue, ContextClueProvider,
|
BARE_POPUP_CONTEXT_CLUES, CONFIRMATION_PROMPT_CONTEXT_CLUES, ContextClue, ContextClueProvider,
|
||||||
|
SYSTEM_TASKS_CONTEXT_CLUES,
|
||||||
};
|
};
|
||||||
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
||||||
use crate::models::Route;
|
use crate::models::Route;
|
||||||
@@ -73,7 +74,9 @@ impl ContextClueProvider for LidarrContextClueProvider {
|
|||||||
.get_active_route_contextual_help(),
|
.get_active_route_contextual_help(),
|
||||||
ActiveLidarrBlock::AddArtistSearchInput
|
ActiveLidarrBlock::AddArtistSearchInput
|
||||||
| ActiveLidarrBlock::AddArtistEmptySearchResults
|
| ActiveLidarrBlock::AddArtistEmptySearchResults
|
||||||
| ActiveLidarrBlock::TestAllIndexers => Some(&BARE_POPUP_CONTEXT_CLUES),
|
| ActiveLidarrBlock::TestAllIndexers
|
||||||
|
| ActiveLidarrBlock::SystemLogs
|
||||||
|
| ActiveLidarrBlock::SystemUpdates => Some(&BARE_POPUP_CONTEXT_CLUES),
|
||||||
_ if EDIT_ARTIST_BLOCKS.contains(&active_lidarr_block)
|
_ if EDIT_ARTIST_BLOCKS.contains(&active_lidarr_block)
|
||||||
|| EDIT_INDEXER_BLOCKS.contains(&active_lidarr_block)
|
|| EDIT_INDEXER_BLOCKS.contains(&active_lidarr_block)
|
||||||
|| INDEXER_SETTINGS_BLOCKS.contains(&active_lidarr_block)
|
|| INDEXER_SETTINGS_BLOCKS.contains(&active_lidarr_block)
|
||||||
@@ -92,6 +95,7 @@ impl ContextClueProvider for LidarrContextClueProvider {
|
|||||||
_ if ADD_ARTIST_BLOCKS.contains(&active_lidarr_block) => {
|
_ if ADD_ARTIST_BLOCKS.contains(&active_lidarr_block) => {
|
||||||
Some(&ADD_ARTIST_SEARCH_RESULTS_CONTEXT_CLUES)
|
Some(&ADD_ARTIST_SEARCH_RESULTS_CONTEXT_CLUES)
|
||||||
}
|
}
|
||||||
|
ActiveLidarrBlock::SystemTasks => Some(&SYSTEM_TASKS_CONTEXT_CLUES),
|
||||||
_ => app
|
_ => app
|
||||||
.data
|
.data
|
||||||
.lidarr_data
|
.lidarr_data
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ mod tests {
|
|||||||
use crate::app::App;
|
use crate::app::App;
|
||||||
use crate::app::context_clues::{
|
use crate::app::context_clues::{
|
||||||
BARE_POPUP_CONTEXT_CLUES, CONFIRMATION_PROMPT_CONTEXT_CLUES, ContextClue, ContextClueProvider,
|
BARE_POPUP_CONTEXT_CLUES, CONFIRMATION_PROMPT_CONTEXT_CLUES, ContextClue, ContextClueProvider,
|
||||||
|
SYSTEM_TASKS_CONTEXT_CLUES,
|
||||||
};
|
};
|
||||||
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
||||||
use crate::app::lidarr::lidarr_context_clues::{
|
use crate::app::lidarr::lidarr_context_clues::{
|
||||||
@@ -266,4 +267,14 @@ mod tests {
|
|||||||
|
|
||||||
assert_some_eq_x!(context_clues, &CONFIRMATION_PROMPT_CONTEXT_CLUES);
|
assert_some_eq_x!(context_clues, &CONFIRMATION_PROMPT_CONTEXT_CLUES);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sonarr_context_clue_provider_system_tasks_clues() {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::SystemTasks.into());
|
||||||
|
let context_clues = LidarrContextClueProvider::get_context_clues(&mut app);
|
||||||
|
|
||||||
|
assert_some_eq_x!(context_clues, &SYSTEM_TASKS_CONTEXT_CLUES);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -210,6 +210,45 @@ mod tests {
|
|||||||
assert_eq!(app.tick_count, 0);
|
assert_eq!(app.tick_count, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_dispatch_by_system_block() {
|
||||||
|
let (tx, mut rx) = mpsc::channel::<NetworkEvent>(500);
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.data.lidarr_data.prompt_confirm = true;
|
||||||
|
app.network_tx = Some(tx);
|
||||||
|
|
||||||
|
app
|
||||||
|
.dispatch_by_lidarr_block(&ActiveLidarrBlock::System)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert!(app.is_loading);
|
||||||
|
assert_eq!(rx.recv().await.unwrap(), LidarrEvent::GetTasks.into());
|
||||||
|
assert_eq!(
|
||||||
|
rx.recv().await.unwrap(),
|
||||||
|
LidarrEvent::GetQueuedEvents.into()
|
||||||
|
);
|
||||||
|
assert_eq!(rx.recv().await.unwrap(), LidarrEvent::GetLogs(500).into());
|
||||||
|
assert!(!app.data.lidarr_data.prompt_confirm);
|
||||||
|
assert_eq!(app.tick_count, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_dispatch_by_system_updates_block() {
|
||||||
|
let (tx, mut rx) = mpsc::channel::<NetworkEvent>(500);
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.data.lidarr_data.prompt_confirm = true;
|
||||||
|
app.network_tx = Some(tx);
|
||||||
|
|
||||||
|
app
|
||||||
|
.dispatch_by_lidarr_block(&ActiveLidarrBlock::SystemUpdates)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert!(app.is_loading);
|
||||||
|
assert_eq!(rx.recv().await.unwrap(), LidarrEvent::GetUpdates.into());
|
||||||
|
assert!(!app.data.lidarr_data.prompt_confirm);
|
||||||
|
assert_eq!(app.tick_count, 0);
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_check_for_lidarr_prompt_action_no_prompt_confirm() {
|
async fn test_check_for_lidarr_prompt_action_no_prompt_confirm() {
|
||||||
let mut app = App::test_default();
|
let mut app = App::test_default();
|
||||||
|
|||||||
@@ -80,6 +80,22 @@ impl App<'_> {
|
|||||||
.dispatch_network_event(LidarrEvent::TestAllIndexers.into())
|
.dispatch_network_event(LidarrEvent::TestAllIndexers.into())
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
ActiveLidarrBlock::System => {
|
||||||
|
self
|
||||||
|
.dispatch_network_event(LidarrEvent::GetTasks.into())
|
||||||
|
.await;
|
||||||
|
self
|
||||||
|
.dispatch_network_event(LidarrEvent::GetQueuedEvents.into())
|
||||||
|
.await;
|
||||||
|
self
|
||||||
|
.dispatch_network_event(LidarrEvent::GetLogs(500).into())
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
ActiveLidarrBlock::SystemUpdates => {
|
||||||
|
self
|
||||||
|
.dispatch_network_event(LidarrEvent::GetUpdates.into())
|
||||||
|
.await;
|
||||||
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use crate::app::App;
|
use crate::app::App;
|
||||||
use crate::app::context_clues::{
|
use crate::app::context_clues::{
|
||||||
BARE_POPUP_CONTEXT_CLUES, CONFIRMATION_PROMPT_CONTEXT_CLUES, ContextClue, ContextClueProvider,
|
BARE_POPUP_CONTEXT_CLUES, CONFIRMATION_PROMPT_CONTEXT_CLUES, ContextClue, ContextClueProvider,
|
||||||
|
SYSTEM_TASKS_CONTEXT_CLUES,
|
||||||
};
|
};
|
||||||
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
||||||
use crate::models::Route;
|
use crate::models::Route;
|
||||||
@@ -82,11 +83,6 @@ pub static ADD_MOVIE_SEARCH_RESULTS_CONTEXT_CLUES: [ContextClue; 2] = [
|
|||||||
(DEFAULT_KEYBINDINGS.esc, "edit search"),
|
(DEFAULT_KEYBINDINGS.esc, "edit search"),
|
||||||
];
|
];
|
||||||
|
|
||||||
pub static SYSTEM_TASKS_CONTEXT_CLUES: [ContextClue; 2] = [
|
|
||||||
(DEFAULT_KEYBINDINGS.submit, "start task"),
|
|
||||||
(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc),
|
|
||||||
];
|
|
||||||
|
|
||||||
pub static COLLECTION_DETAILS_CONTEXT_CLUES: [ContextClue; 3] = [
|
pub static COLLECTION_DETAILS_CONTEXT_CLUES: [ContextClue; 3] = [
|
||||||
(DEFAULT_KEYBINDINGS.submit, "show overview/add movie"),
|
(DEFAULT_KEYBINDINGS.submit, "show overview/add movie"),
|
||||||
(DEFAULT_KEYBINDINGS.edit, "edit collection"),
|
(DEFAULT_KEYBINDINGS.edit, "edit collection"),
|
||||||
|
|||||||
@@ -5,12 +5,13 @@ mod tests {
|
|||||||
BARE_POPUP_CONTEXT_CLUES, BLOCKLIST_CONTEXT_CLUES, CONFIRMATION_PROMPT_CONTEXT_CLUES,
|
BARE_POPUP_CONTEXT_CLUES, BLOCKLIST_CONTEXT_CLUES, CONFIRMATION_PROMPT_CONTEXT_CLUES,
|
||||||
ContextClue, ContextClueProvider, DOWNLOADS_CONTEXT_CLUES, HISTORY_CONTEXT_CLUES,
|
ContextClue, ContextClueProvider, DOWNLOADS_CONTEXT_CLUES, HISTORY_CONTEXT_CLUES,
|
||||||
INDEXERS_CONTEXT_CLUES, ROOT_FOLDERS_CONTEXT_CLUES, SYSTEM_CONTEXT_CLUES,
|
INDEXERS_CONTEXT_CLUES, ROOT_FOLDERS_CONTEXT_CLUES, SYSTEM_CONTEXT_CLUES,
|
||||||
|
SYSTEM_TASKS_CONTEXT_CLUES,
|
||||||
};
|
};
|
||||||
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
||||||
use crate::app::radarr::radarr_context_clues::{
|
use crate::app::radarr::radarr_context_clues::{
|
||||||
ADD_MOVIE_SEARCH_RESULTS_CONTEXT_CLUES, COLLECTION_DETAILS_CONTEXT_CLUES,
|
ADD_MOVIE_SEARCH_RESULTS_CONTEXT_CLUES, COLLECTION_DETAILS_CONTEXT_CLUES,
|
||||||
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, RadarrContextClueProvider, SYSTEM_TASKS_CONTEXT_CLUES,
|
MOVIE_DETAILS_CONTEXT_CLUES, RadarrContextClueProvider,
|
||||||
};
|
};
|
||||||
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, RadarrData};
|
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, RadarrData};
|
||||||
use crate::models::servarr_data::sonarr::sonarr_data::ActiveSonarrBlock;
|
use crate::models::servarr_data::sonarr::sonarr_data::ActiveSonarrBlock;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use crate::app::context_clues::{
|
use crate::app::context_clues::{
|
||||||
BARE_POPUP_CONTEXT_CLUES, CONFIRMATION_PROMPT_CONTEXT_CLUES, ContextClueProvider,
|
BARE_POPUP_CONTEXT_CLUES, CONFIRMATION_PROMPT_CONTEXT_CLUES, ContextClueProvider,
|
||||||
|
SYSTEM_TASKS_CONTEXT_CLUES,
|
||||||
};
|
};
|
||||||
use crate::app::{App, context_clues::ContextClue, key_binding::DEFAULT_KEYBINDINGS};
|
use crate::app::{App, context_clues::ContextClue, key_binding::DEFAULT_KEYBINDINGS};
|
||||||
use crate::models::Route;
|
use crate::models::Route;
|
||||||
@@ -163,11 +164,6 @@ pub static SELECTABLE_EPISODE_DETAILS_CONTEXT_CLUES: [ContextClue; 4] = [
|
|||||||
(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc),
|
(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc),
|
||||||
];
|
];
|
||||||
|
|
||||||
pub static SYSTEM_TASKS_CONTEXT_CLUES: [ContextClue; 2] = [
|
|
||||||
(DEFAULT_KEYBINDINGS.submit, "start task"),
|
|
||||||
(DEFAULT_KEYBINDINGS.esc, DEFAULT_KEYBINDINGS.esc.desc),
|
|
||||||
];
|
|
||||||
|
|
||||||
pub(in crate::app) struct SonarrContextClueProvider;
|
pub(in crate::app) struct SonarrContextClueProvider;
|
||||||
|
|
||||||
impl ContextClueProvider for SonarrContextClueProvider {
|
impl ContextClueProvider for SonarrContextClueProvider {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ mod tests {
|
|||||||
BARE_POPUP_CONTEXT_CLUES, BLOCKLIST_CONTEXT_CLUES, CONFIRMATION_PROMPT_CONTEXT_CLUES,
|
BARE_POPUP_CONTEXT_CLUES, BLOCKLIST_CONTEXT_CLUES, CONFIRMATION_PROMPT_CONTEXT_CLUES,
|
||||||
ContextClue, ContextClueProvider, DOWNLOADS_CONTEXT_CLUES, HISTORY_CONTEXT_CLUES,
|
ContextClue, ContextClueProvider, DOWNLOADS_CONTEXT_CLUES, HISTORY_CONTEXT_CLUES,
|
||||||
INDEXERS_CONTEXT_CLUES, ROOT_FOLDERS_CONTEXT_CLUES, SYSTEM_CONTEXT_CLUES,
|
INDEXERS_CONTEXT_CLUES, ROOT_FOLDERS_CONTEXT_CLUES, SYSTEM_CONTEXT_CLUES,
|
||||||
|
SYSTEM_TASKS_CONTEXT_CLUES,
|
||||||
};
|
};
|
||||||
use crate::app::sonarr::sonarr_context_clues::{
|
use crate::app::sonarr::sonarr_context_clues::{
|
||||||
SELECTABLE_EPISODE_DETAILS_CONTEXT_CLUES, SonarrContextClueProvider,
|
SELECTABLE_EPISODE_DETAILS_CONTEXT_CLUES, SonarrContextClueProvider,
|
||||||
@@ -15,7 +16,7 @@ mod tests {
|
|||||||
ADD_SERIES_SEARCH_RESULTS_CONTEXT_CLUES, EPISODE_DETAILS_CONTEXT_CLUES,
|
ADD_SERIES_SEARCH_RESULTS_CONTEXT_CLUES, EPISODE_DETAILS_CONTEXT_CLUES,
|
||||||
MANUAL_EPISODE_SEARCH_CONTEXT_CLUES, MANUAL_SEASON_SEARCH_CONTEXT_CLUES,
|
MANUAL_EPISODE_SEARCH_CONTEXT_CLUES, MANUAL_SEASON_SEARCH_CONTEXT_CLUES,
|
||||||
SEASON_DETAILS_CONTEXT_CLUES, SEASON_HISTORY_CONTEXT_CLUES, SERIES_CONTEXT_CLUES,
|
SEASON_DETAILS_CONTEXT_CLUES, SEASON_HISTORY_CONTEXT_CLUES, SERIES_CONTEXT_CLUES,
|
||||||
SERIES_DETAILS_CONTEXT_CLUES, SERIES_HISTORY_CONTEXT_CLUES, SYSTEM_TASKS_CONTEXT_CLUES,
|
SERIES_DETAILS_CONTEXT_CLUES, SERIES_HISTORY_CONTEXT_CLUES,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock;
|
use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock;
|
||||||
|
|||||||
@@ -134,6 +134,44 @@ mod tests {
|
|||||||
assert_ok!(&result);
|
assert_ok!(&result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_start_task_requires_task_name() {
|
||||||
|
let result = Cli::command().try_get_matches_from(["managarr", "sonarr", "start-task"]);
|
||||||
|
|
||||||
|
assert_err!(&result);
|
||||||
|
assert_eq!(
|
||||||
|
result.unwrap_err().kind(),
|
||||||
|
ErrorKind::MissingRequiredArgument
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_start_task_task_name_validation() {
|
||||||
|
let result = Cli::command().try_get_matches_from([
|
||||||
|
"managarr",
|
||||||
|
"lidarr",
|
||||||
|
"start-task",
|
||||||
|
"--task-name",
|
||||||
|
"test",
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert_err!(&result);
|
||||||
|
assert_eq!(result.unwrap_err().kind(), ErrorKind::InvalidValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_start_task_requirements_satisfied() {
|
||||||
|
let result = Cli::command().try_get_matches_from([
|
||||||
|
"managarr",
|
||||||
|
"lidarr",
|
||||||
|
"start-task",
|
||||||
|
"--task-name",
|
||||||
|
"application-update-check",
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert_ok!(&result);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_mark_history_item_as_failed_requires_history_item_id() {
|
fn test_mark_history_item_as_failed_requires_history_item_id() {
|
||||||
let result =
|
let result =
|
||||||
@@ -196,6 +234,7 @@ mod tests {
|
|||||||
use crate::cli::lidarr::get_command_handler::LidarrGetCommand;
|
use crate::cli::lidarr::get_command_handler::LidarrGetCommand;
|
||||||
use crate::cli::lidarr::refresh_command_handler::LidarrRefreshCommand;
|
use crate::cli::lidarr::refresh_command_handler::LidarrRefreshCommand;
|
||||||
use crate::cli::lidarr::trigger_automatic_search_command_handler::LidarrTriggerAutomaticSearchCommand;
|
use crate::cli::lidarr::trigger_automatic_search_command_handler::LidarrTriggerAutomaticSearchCommand;
|
||||||
|
use crate::models::lidarr_models::LidarrTaskName;
|
||||||
use crate::models::servarr_models::IndexerSettings;
|
use crate::models::servarr_models::IndexerSettings;
|
||||||
use crate::{
|
use crate::{
|
||||||
app::App,
|
app::App,
|
||||||
@@ -484,6 +523,33 @@ mod tests {
|
|||||||
assert_ok!(&result);
|
assert_ok!(&result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_start_task_command() {
|
||||||
|
let expected_task_name = LidarrTaskName::ApplicationUpdateCheck;
|
||||||
|
let mut mock_network = MockNetworkTrait::new();
|
||||||
|
mock_network
|
||||||
|
.expect_handle_network_event()
|
||||||
|
.with(eq::<NetworkEvent>(
|
||||||
|
LidarrEvent::StartTask(expected_task_name).into(),
|
||||||
|
))
|
||||||
|
.times(1)
|
||||||
|
.returning(|_| {
|
||||||
|
Ok(Serdeable::Lidarr(LidarrSerdeable::Value(
|
||||||
|
json!({"testResponse": "response"}),
|
||||||
|
)))
|
||||||
|
});
|
||||||
|
let app_arc = Arc::new(Mutex::new(App::test_default()));
|
||||||
|
let start_task_command = LidarrCommand::StartTask {
|
||||||
|
task_name: LidarrTaskName::ApplicationUpdateCheck,
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = LidarrCliHandler::with(&app_arc, start_task_command, &mut mock_network)
|
||||||
|
.handle()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert_ok!(&result);
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_test_indexer_command() {
|
async fn test_test_indexer_command() {
|
||||||
let expected_indexer_id = 1;
|
let expected_indexer_id = 1;
|
||||||
|
|||||||
@@ -41,14 +41,30 @@ pub enum LidarrListCommand {
|
|||||||
},
|
},
|
||||||
#[command(about = "List all Lidarr indexers")]
|
#[command(about = "List all Lidarr indexers")]
|
||||||
Indexers,
|
Indexers,
|
||||||
|
#[command(about = "Fetch Lidarr logs")]
|
||||||
|
Logs {
|
||||||
|
#[arg(long, help = "How many log events to fetch", default_value_t = 500)]
|
||||||
|
events: u64,
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "Output the logs in the same format as they appear in the log files"
|
||||||
|
)]
|
||||||
|
output_in_log_format: bool,
|
||||||
|
},
|
||||||
#[command(about = "List all Lidarr metadata profiles")]
|
#[command(about = "List all Lidarr metadata profiles")]
|
||||||
MetadataProfiles,
|
MetadataProfiles,
|
||||||
#[command(about = "List all Lidarr quality profiles")]
|
#[command(about = "List all Lidarr quality profiles")]
|
||||||
QualityProfiles,
|
QualityProfiles,
|
||||||
|
#[command(about = "List all queued events")]
|
||||||
|
QueuedEvents,
|
||||||
#[command(about = "List all root folders in Lidarr")]
|
#[command(about = "List all root folders in Lidarr")]
|
||||||
RootFolders,
|
RootFolders,
|
||||||
#[command(about = "List all Lidarr tags")]
|
#[command(about = "List all Lidarr tags")]
|
||||||
Tags,
|
Tags,
|
||||||
|
#[command(about = "List all Lidarr tasks")]
|
||||||
|
Tasks,
|
||||||
|
#[command(about = "List all Lidarr updates")]
|
||||||
|
Updates,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<LidarrListCommand> for Command {
|
impl From<LidarrListCommand> for Command {
|
||||||
@@ -58,7 +74,7 @@ impl From<LidarrListCommand> for Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(super) struct LidarrListCommandHandler<'a, 'b> {
|
pub(super) struct LidarrListCommandHandler<'a, 'b> {
|
||||||
_app: &'a Arc<Mutex<App<'b>>>,
|
app: &'a Arc<Mutex<App<'b>>>,
|
||||||
command: LidarrListCommand,
|
command: LidarrListCommand,
|
||||||
network: &'a mut dyn NetworkTrait,
|
network: &'a mut dyn NetworkTrait,
|
||||||
}
|
}
|
||||||
@@ -70,7 +86,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, LidarrListCommand> for LidarrListCommandH
|
|||||||
network: &'a mut dyn NetworkTrait,
|
network: &'a mut dyn NetworkTrait,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
LidarrListCommandHandler {
|
LidarrListCommandHandler {
|
||||||
_app: app,
|
app,
|
||||||
command,
|
command,
|
||||||
network,
|
network,
|
||||||
}
|
}
|
||||||
@@ -113,6 +129,23 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, LidarrListCommand> for LidarrListCommandH
|
|||||||
.await?;
|
.await?;
|
||||||
serde_json::to_string_pretty(&resp)?
|
serde_json::to_string_pretty(&resp)?
|
||||||
}
|
}
|
||||||
|
LidarrListCommand::Logs {
|
||||||
|
events,
|
||||||
|
output_in_log_format,
|
||||||
|
} => {
|
||||||
|
let logs = self
|
||||||
|
.network
|
||||||
|
.handle_network_event(LidarrEvent::GetLogs(events).into())
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if output_in_log_format {
|
||||||
|
let log_lines = &self.app.lock().await.data.sonarr_data.logs.items;
|
||||||
|
|
||||||
|
serde_json::to_string_pretty(log_lines)?
|
||||||
|
} else {
|
||||||
|
serde_json::to_string_pretty(&logs)?
|
||||||
|
}
|
||||||
|
}
|
||||||
LidarrListCommand::MetadataProfiles => {
|
LidarrListCommand::MetadataProfiles => {
|
||||||
let resp = self
|
let resp = self
|
||||||
.network
|
.network
|
||||||
@@ -127,6 +160,13 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, LidarrListCommand> for LidarrListCommandH
|
|||||||
.await?;
|
.await?;
|
||||||
serde_json::to_string_pretty(&resp)?
|
serde_json::to_string_pretty(&resp)?
|
||||||
}
|
}
|
||||||
|
LidarrListCommand::QueuedEvents => {
|
||||||
|
let resp = self
|
||||||
|
.network
|
||||||
|
.handle_network_event(LidarrEvent::GetQueuedEvents.into())
|
||||||
|
.await?;
|
||||||
|
serde_json::to_string_pretty(&resp)?
|
||||||
|
}
|
||||||
LidarrListCommand::RootFolders => {
|
LidarrListCommand::RootFolders => {
|
||||||
let resp = self
|
let resp = self
|
||||||
.network
|
.network
|
||||||
@@ -141,6 +181,20 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, LidarrListCommand> for LidarrListCommandH
|
|||||||
.await?;
|
.await?;
|
||||||
serde_json::to_string_pretty(&resp)?
|
serde_json::to_string_pretty(&resp)?
|
||||||
}
|
}
|
||||||
|
LidarrListCommand::Tasks => {
|
||||||
|
let resp = self
|
||||||
|
.network
|
||||||
|
.handle_network_event(LidarrEvent::GetTasks.into())
|
||||||
|
.await?;
|
||||||
|
serde_json::to_string_pretty(&resp)?
|
||||||
|
}
|
||||||
|
LidarrListCommand::Updates => {
|
||||||
|
let resp = self
|
||||||
|
.network
|
||||||
|
.handle_network_event(LidarrEvent::GetUpdates.into())
|
||||||
|
.await?;
|
||||||
|
serde_json::to_string_pretty(&resp)?
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
|
|||||||
@@ -30,7 +30,10 @@ mod tests {
|
|||||||
"indexers",
|
"indexers",
|
||||||
"metadata-profiles",
|
"metadata-profiles",
|
||||||
"quality-profiles",
|
"quality-profiles",
|
||||||
|
"queued-events",
|
||||||
"tags",
|
"tags",
|
||||||
|
"tasks",
|
||||||
|
"updates",
|
||||||
"root-folders"
|
"root-folders"
|
||||||
)]
|
)]
|
||||||
subcommand: &str,
|
subcommand: &str,
|
||||||
@@ -111,6 +114,31 @@ mod tests {
|
|||||||
};
|
};
|
||||||
assert_eq!(history_command, expected_args);
|
assert_eq!(history_command, expected_args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_list_logs_events_flag_requires_arguments() {
|
||||||
|
let result =
|
||||||
|
Cli::command().try_get_matches_from(["managarr", "lidarr", "list", "logs", "--events"]);
|
||||||
|
|
||||||
|
assert_err!(&result);
|
||||||
|
assert_eq!(result.unwrap_err().kind(), ErrorKind::InvalidValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_list_logs_default_values() {
|
||||||
|
let expected_args = LidarrListCommand::Logs {
|
||||||
|
events: 500,
|
||||||
|
output_in_log_format: false,
|
||||||
|
};
|
||||||
|
let result = Cli::try_parse_from(["managarr", "lidarr", "list", "logs"]);
|
||||||
|
|
||||||
|
assert_ok!(&result);
|
||||||
|
|
||||||
|
let Some(Command::Lidarr(LidarrCommand::List(logs_command))) = result.unwrap().command else {
|
||||||
|
panic!("Unexpected command type");
|
||||||
|
};
|
||||||
|
assert_eq!(logs_command, expected_args);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod handler {
|
mod handler {
|
||||||
@@ -136,8 +164,11 @@ mod tests {
|
|||||||
#[case(LidarrListCommand::Indexers, LidarrEvent::GetIndexers)]
|
#[case(LidarrListCommand::Indexers, LidarrEvent::GetIndexers)]
|
||||||
#[case(LidarrListCommand::MetadataProfiles, LidarrEvent::GetMetadataProfiles)]
|
#[case(LidarrListCommand::MetadataProfiles, LidarrEvent::GetMetadataProfiles)]
|
||||||
#[case(LidarrListCommand::QualityProfiles, LidarrEvent::GetQualityProfiles)]
|
#[case(LidarrListCommand::QualityProfiles, LidarrEvent::GetQualityProfiles)]
|
||||||
|
#[case(LidarrListCommand::QueuedEvents, LidarrEvent::GetQueuedEvents)]
|
||||||
#[case(LidarrListCommand::RootFolders, LidarrEvent::GetRootFolders)]
|
#[case(LidarrListCommand::RootFolders, LidarrEvent::GetRootFolders)]
|
||||||
#[case(LidarrListCommand::Tags, LidarrEvent::GetTags)]
|
#[case(LidarrListCommand::Tags, LidarrEvent::GetTags)]
|
||||||
|
#[case(LidarrListCommand::Tasks, LidarrEvent::GetTasks)]
|
||||||
|
#[case(LidarrListCommand::Updates, LidarrEvent::GetUpdates)]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_handle_list_command(
|
async fn test_handle_list_command(
|
||||||
#[case] list_command: LidarrListCommand,
|
#[case] list_command: LidarrListCommand,
|
||||||
@@ -235,5 +266,33 @@ mod tests {
|
|||||||
|
|
||||||
assert_ok!(&result);
|
assert_ok!(&result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_handle_list_logs_command() {
|
||||||
|
let expected_events = 1000;
|
||||||
|
let mut mock_network = MockNetworkTrait::new();
|
||||||
|
mock_network
|
||||||
|
.expect_handle_network_event()
|
||||||
|
.with(eq::<NetworkEvent>(
|
||||||
|
LidarrEvent::GetLogs(expected_events).into(),
|
||||||
|
))
|
||||||
|
.times(1)
|
||||||
|
.returning(|_| {
|
||||||
|
Ok(Serdeable::Lidarr(LidarrSerdeable::Value(
|
||||||
|
json!({"testResponse": "response"}),
|
||||||
|
)))
|
||||||
|
});
|
||||||
|
let app_arc = Arc::new(Mutex::new(App::test_default()));
|
||||||
|
let list_logs_command = LidarrListCommand::Logs {
|
||||||
|
events: 1000,
|
||||||
|
output_in_log_format: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = LidarrListCommandHandler::with(&app_arc, list_logs_command, &mut mock_network)
|
||||||
|
.handle()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert_ok!(&result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-2
@@ -14,11 +14,11 @@ use trigger_automatic_search_command_handler::{
|
|||||||
LidarrTriggerAutomaticSearchCommand, LidarrTriggerAutomaticSearchCommandHandler,
|
LidarrTriggerAutomaticSearchCommand, LidarrTriggerAutomaticSearchCommandHandler,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::{CliCommandHandler, Command};
|
||||||
|
use crate::models::lidarr_models::LidarrTaskName;
|
||||||
use crate::network::lidarr_network::LidarrEvent;
|
use crate::network::lidarr_network::LidarrEvent;
|
||||||
use crate::{app::App, network::NetworkTrait};
|
use crate::{app::App, network::NetworkTrait};
|
||||||
|
|
||||||
use super::{CliCommandHandler, Command};
|
|
||||||
|
|
||||||
mod add_command_handler;
|
mod add_command_handler;
|
||||||
mod delete_command_handler;
|
mod delete_command_handler;
|
||||||
mod edit_command_handler;
|
mod edit_command_handler;
|
||||||
@@ -86,6 +86,16 @@ pub enum LidarrCommand {
|
|||||||
)]
|
)]
|
||||||
query: String,
|
query: String,
|
||||||
},
|
},
|
||||||
|
#[command(about = "Start the specified Lidarr task")]
|
||||||
|
StartTask {
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "The name of the task to trigger",
|
||||||
|
value_enum,
|
||||||
|
required = true
|
||||||
|
)]
|
||||||
|
task_name: LidarrTaskName,
|
||||||
|
},
|
||||||
#[command(
|
#[command(
|
||||||
about = "Test the indexer with the given ID. Note that a successful test returns an empty JSON body; i.e. '{}'"
|
about = "Test the indexer with the given ID. Note that a successful test returns an empty JSON body; i.e. '{}'"
|
||||||
)]
|
)]
|
||||||
@@ -199,6 +209,13 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, LidarrCommand> for LidarrCliHandler<'a, '
|
|||||||
.await?;
|
.await?;
|
||||||
serde_json::to_string_pretty(&resp)?
|
serde_json::to_string_pretty(&resp)?
|
||||||
}
|
}
|
||||||
|
LidarrCommand::StartTask { task_name } => {
|
||||||
|
let resp = self
|
||||||
|
.network
|
||||||
|
.handle_network_event(LidarrEvent::StartTask(task_name).into())
|
||||||
|
.await?;
|
||||||
|
serde_json::to_string_pretty(&resp)?
|
||||||
|
}
|
||||||
LidarrCommand::TestIndexer { indexer_id } => {
|
LidarrCommand::TestIndexer { indexer_id } => {
|
||||||
let resp = self
|
let resp = self
|
||||||
.network
|
.network
|
||||||
|
|||||||
@@ -101,9 +101,9 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
app.data.lidarr_data.main_tabs.get_active_route(),
|
app.data.lidarr_data.main_tabs.get_active_route(),
|
||||||
ActiveLidarrBlock::Artists.into()
|
ActiveLidarrBlock::System.into()
|
||||||
);
|
);
|
||||||
assert_navigation_pushed!(app, ActiveLidarrBlock::Artists.into());
|
assert_navigation_pushed!(app, ActiveLidarrBlock::System.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
|
|||||||
@@ -52,11 +52,12 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
#[case(0, ActiveLidarrBlock::Indexers, ActiveLidarrBlock::Downloads)]
|
#[case(0, ActiveLidarrBlock::System, ActiveLidarrBlock::Downloads)]
|
||||||
#[case(1, ActiveLidarrBlock::Artists, ActiveLidarrBlock::History)]
|
#[case(1, ActiveLidarrBlock::Artists, ActiveLidarrBlock::History)]
|
||||||
#[case(2, ActiveLidarrBlock::Downloads, ActiveLidarrBlock::RootFolders)]
|
#[case(2, ActiveLidarrBlock::Downloads, ActiveLidarrBlock::RootFolders)]
|
||||||
#[case(3, ActiveLidarrBlock::History, ActiveLidarrBlock::Indexers)]
|
#[case(3, ActiveLidarrBlock::History, ActiveLidarrBlock::Indexers)]
|
||||||
#[case(4, ActiveLidarrBlock::RootFolders, ActiveLidarrBlock::Artists)]
|
#[case(4, ActiveLidarrBlock::RootFolders, ActiveLidarrBlock::System)]
|
||||||
|
#[case(5, ActiveLidarrBlock::Indexers, ActiveLidarrBlock::Artists)]
|
||||||
fn test_lidarr_handler_change_tab_left_right_keys(
|
fn test_lidarr_handler_change_tab_left_right_keys(
|
||||||
#[case] index: usize,
|
#[case] index: usize,
|
||||||
#[case] left_block: ActiveLidarrBlock,
|
#[case] left_block: ActiveLidarrBlock,
|
||||||
@@ -85,11 +86,12 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
#[case(0, ActiveLidarrBlock::Indexers, ActiveLidarrBlock::Downloads)]
|
#[case(0, ActiveLidarrBlock::System, ActiveLidarrBlock::Downloads)]
|
||||||
#[case(1, ActiveLidarrBlock::Artists, ActiveLidarrBlock::History)]
|
#[case(1, ActiveLidarrBlock::Artists, ActiveLidarrBlock::History)]
|
||||||
#[case(2, ActiveLidarrBlock::Downloads, ActiveLidarrBlock::RootFolders)]
|
#[case(2, ActiveLidarrBlock::Downloads, ActiveLidarrBlock::RootFolders)]
|
||||||
#[case(3, ActiveLidarrBlock::History, ActiveLidarrBlock::Indexers)]
|
#[case(3, ActiveLidarrBlock::History, ActiveLidarrBlock::Indexers)]
|
||||||
#[case(4, ActiveLidarrBlock::RootFolders, ActiveLidarrBlock::Artists)]
|
#[case(4, ActiveLidarrBlock::RootFolders, ActiveLidarrBlock::System)]
|
||||||
|
#[case(5, ActiveLidarrBlock::Indexers, ActiveLidarrBlock::Artists)]
|
||||||
fn test_lidarr_handler_change_tab_left_right_keys_alt_navigation(
|
fn test_lidarr_handler_change_tab_left_right_keys_alt_navigation(
|
||||||
#[case] index: usize,
|
#[case] index: usize,
|
||||||
#[case] left_block: ActiveLidarrBlock,
|
#[case] left_block: ActiveLidarrBlock,
|
||||||
@@ -123,6 +125,7 @@ mod tests {
|
|||||||
#[case(2, ActiveLidarrBlock::History)]
|
#[case(2, ActiveLidarrBlock::History)]
|
||||||
#[case(3, ActiveLidarrBlock::RootFolders)]
|
#[case(3, ActiveLidarrBlock::RootFolders)]
|
||||||
#[case(4, ActiveLidarrBlock::Indexers)]
|
#[case(4, ActiveLidarrBlock::Indexers)]
|
||||||
|
#[case(5, ActiveLidarrBlock::System)]
|
||||||
fn test_lidarr_handler_change_tab_left_right_keys_alt_navigation_no_op_when_ignoring_quit_key(
|
fn test_lidarr_handler_change_tab_left_right_keys_alt_navigation_no_op_when_ignoring_quit_key(
|
||||||
#[case] index: usize,
|
#[case] index: usize,
|
||||||
#[case] block: ActiveLidarrBlock,
|
#[case] block: ActiveLidarrBlock,
|
||||||
@@ -250,4 +253,23 @@ mod tests {
|
|||||||
active_sonarr_block
|
active_sonarr_block
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn test_delegates_system_blocks_to_system_handler(
|
||||||
|
#[values(
|
||||||
|
ActiveLidarrBlock::System,
|
||||||
|
ActiveLidarrBlock::SystemLogs,
|
||||||
|
ActiveLidarrBlock::SystemQueuedEvents,
|
||||||
|
ActiveLidarrBlock::SystemTasks,
|
||||||
|
ActiveLidarrBlock::SystemTaskStartConfirmPrompt,
|
||||||
|
ActiveLidarrBlock::SystemUpdates
|
||||||
|
)]
|
||||||
|
active_sonarr_block: ActiveLidarrBlock,
|
||||||
|
) {
|
||||||
|
test_handler_delegation!(
|
||||||
|
LidarrHandler,
|
||||||
|
ActiveLidarrBlock::System,
|
||||||
|
active_sonarr_block
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use library::LibraryHandler;
|
|||||||
use super::KeyEventHandler;
|
use super::KeyEventHandler;
|
||||||
use crate::handlers::lidarr_handlers::downloads::DownloadsHandler;
|
use crate::handlers::lidarr_handlers::downloads::DownloadsHandler;
|
||||||
use crate::handlers::lidarr_handlers::root_folders::RootFoldersHandler;
|
use crate::handlers::lidarr_handlers::root_folders::RootFoldersHandler;
|
||||||
|
use crate::handlers::lidarr_handlers::system::SystemHandler;
|
||||||
use crate::models::Route;
|
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,
|
||||||
@@ -14,11 +15,12 @@ mod downloads;
|
|||||||
mod history;
|
mod history;
|
||||||
mod indexers;
|
mod indexers;
|
||||||
mod library;
|
mod library;
|
||||||
|
mod root_folders;
|
||||||
|
mod system;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[path = "lidarr_handler_tests.rs"]
|
#[path = "lidarr_handler_tests.rs"]
|
||||||
mod lidarr_handler_tests;
|
mod lidarr_handler_tests;
|
||||||
mod root_folders;
|
|
||||||
|
|
||||||
pub(super) struct LidarrHandler<'a, 'b> {
|
pub(super) struct LidarrHandler<'a, 'b> {
|
||||||
key: Key,
|
key: Key,
|
||||||
@@ -46,6 +48,9 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveLidarrBlock> for LidarrHandler<'a, 'b
|
|||||||
_ if IndexersHandler::accepts(self.active_lidarr_block) => {
|
_ if IndexersHandler::accepts(self.active_lidarr_block) => {
|
||||||
IndexersHandler::new(self.key, self.app, self.active_lidarr_block, self.context).handle();
|
IndexersHandler::new(self.key, self.app, self.active_lidarr_block, self.context).handle();
|
||||||
}
|
}
|
||||||
|
_ if SystemHandler::accepts(self.active_lidarr_block) => {
|
||||||
|
SystemHandler::new(self.key, self.app, self.active_lidarr_block, self.context).handle();
|
||||||
|
}
|
||||||
_ => self.handle_key_event(),
|
_ => self.handle_key_event(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -54,10 +59,6 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveLidarrBlock> for LidarrHandler<'a, 'b
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ignore_special_keys(&self) -> bool {
|
|
||||||
self.app.ignore_special_keys_for_textbox_input
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new(
|
fn new(
|
||||||
key: Key,
|
key: Key,
|
||||||
app: &'a mut App<'b>,
|
app: &'a mut App<'b>,
|
||||||
@@ -76,6 +77,10 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveLidarrBlock> for LidarrHandler<'a, 'b
|
|||||||
self.key
|
self.key
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ignore_special_keys(&self) -> bool {
|
||||||
|
self.app.ignore_special_keys_for_textbox_input
|
||||||
|
}
|
||||||
|
|
||||||
fn is_ready(&self) -> bool {
|
fn is_ready(&self) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,135 @@
|
|||||||
|
use crate::app::App;
|
||||||
|
use crate::event::Key;
|
||||||
|
use crate::handlers::lidarr_handlers::handle_change_tab_left_right_keys;
|
||||||
|
use crate::handlers::lidarr_handlers::system::system_details_handler::SystemDetailsHandler;
|
||||||
|
use crate::handlers::{KeyEventHandler, handle_clear_errors};
|
||||||
|
use crate::matches_key;
|
||||||
|
use crate::models::servarr_data::lidarr::lidarr_data::ActiveLidarrBlock;
|
||||||
|
use crate::models::{Route, Scrollable};
|
||||||
|
|
||||||
|
mod system_details_handler;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[path = "system_handler_tests.rs"]
|
||||||
|
mod system_handler_tests;
|
||||||
|
|
||||||
|
pub(super) struct SystemHandler<'a, 'b> {
|
||||||
|
key: Key,
|
||||||
|
app: &'a mut App<'b>,
|
||||||
|
active_lidarr_block: ActiveLidarrBlock,
|
||||||
|
context: Option<ActiveLidarrBlock>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveLidarrBlock> for SystemHandler<'a, 'b> {
|
||||||
|
fn handle(&mut self) {
|
||||||
|
match self.active_lidarr_block {
|
||||||
|
_ if SystemDetailsHandler::accepts(self.active_lidarr_block) => {
|
||||||
|
SystemDetailsHandler::new(self.key, self.app, self.active_lidarr_block, self.context)
|
||||||
|
.handle()
|
||||||
|
}
|
||||||
|
_ => self.handle_key_event(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn accepts(active_block: ActiveLidarrBlock) -> bool {
|
||||||
|
SystemDetailsHandler::accepts(active_block) || active_block == ActiveLidarrBlock::System
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new(
|
||||||
|
key: Key,
|
||||||
|
app: &'a mut App<'b>,
|
||||||
|
active_block: ActiveLidarrBlock,
|
||||||
|
context: Option<ActiveLidarrBlock>,
|
||||||
|
) -> SystemHandler<'a, 'b> {
|
||||||
|
SystemHandler {
|
||||||
|
key,
|
||||||
|
app,
|
||||||
|
active_lidarr_block: active_block,
|
||||||
|
context,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_key(&self) -> Key {
|
||||||
|
self.key
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ignore_special_keys(&self) -> bool {
|
||||||
|
self.app.ignore_special_keys_for_textbox_input
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_ready(&self) -> bool {
|
||||||
|
!self.app.is_loading
|
||||||
|
&& !self.app.data.lidarr_data.logs.is_empty()
|
||||||
|
&& !self.app.data.lidarr_data.queued_events.is_empty()
|
||||||
|
&& !self.app.data.lidarr_data.tasks.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_scroll_up(&mut self) {}
|
||||||
|
|
||||||
|
fn handle_scroll_down(&mut self) {}
|
||||||
|
|
||||||
|
fn handle_home(&mut self) {}
|
||||||
|
|
||||||
|
fn handle_end(&mut self) {}
|
||||||
|
|
||||||
|
fn handle_delete(&mut self) {}
|
||||||
|
|
||||||
|
fn handle_left_right_action(&mut self) {
|
||||||
|
if self.active_lidarr_block == ActiveLidarrBlock::System {
|
||||||
|
handle_change_tab_left_right_keys(self.app, self.key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_submit(&mut self) {}
|
||||||
|
|
||||||
|
fn handle_esc(&mut self) {
|
||||||
|
handle_clear_errors(self.app)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_char_key_event(&mut self) {
|
||||||
|
if self.active_lidarr_block == ActiveLidarrBlock::System {
|
||||||
|
let key = self.key;
|
||||||
|
match self.key {
|
||||||
|
_ if matches_key!(refresh, key) => {
|
||||||
|
self.app.should_refresh = true;
|
||||||
|
}
|
||||||
|
_ if matches_key!(events, key) => {
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.push_navigation_stack(ActiveLidarrBlock::SystemQueuedEvents.into());
|
||||||
|
}
|
||||||
|
_ if matches_key!(logs, key) => {
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.push_navigation_stack(ActiveLidarrBlock::SystemLogs.into());
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.log_details
|
||||||
|
.set_items(self.app.data.lidarr_data.logs.items.to_vec());
|
||||||
|
self.app.data.lidarr_data.log_details.scroll_to_bottom();
|
||||||
|
}
|
||||||
|
_ if matches_key!(tasks, key) => {
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.push_navigation_stack(ActiveLidarrBlock::SystemTasks.into());
|
||||||
|
}
|
||||||
|
_ if matches_key!(update, key) => {
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.push_navigation_stack(ActiveLidarrBlock::SystemUpdates.into());
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn app_mut(&mut self) -> &mut App<'b> {
|
||||||
|
self.app
|
||||||
|
}
|
||||||
|
|
||||||
|
fn current_route(&self) -> Route {
|
||||||
|
self.app.get_current_route()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,207 @@
|
|||||||
|
use crate::app::App;
|
||||||
|
use crate::event::Key;
|
||||||
|
use crate::handlers::{KeyEventHandler, handle_prompt_toggle};
|
||||||
|
use crate::matches_key;
|
||||||
|
use crate::models::lidarr_models::LidarrTaskName;
|
||||||
|
use crate::models::servarr_data::lidarr::lidarr_data::{ActiveLidarrBlock, SYSTEM_DETAILS_BLOCKS};
|
||||||
|
use crate::models::stateful_list::StatefulList;
|
||||||
|
use crate::models::{Route, Scrollable};
|
||||||
|
use crate::network::lidarr_network::LidarrEvent;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[path = "system_details_handler_tests.rs"]
|
||||||
|
mod system_details_handler_tests;
|
||||||
|
|
||||||
|
pub(super) struct SystemDetailsHandler<'a, 'b> {
|
||||||
|
key: Key,
|
||||||
|
app: &'a mut App<'b>,
|
||||||
|
active_lidarr_block: ActiveLidarrBlock,
|
||||||
|
_context: Option<ActiveLidarrBlock>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SystemDetailsHandler<'_, '_> {
|
||||||
|
fn extract_task_name(&self) -> LidarrTaskName {
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.tasks
|
||||||
|
.current_selection()
|
||||||
|
.task_name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveLidarrBlock> for SystemDetailsHandler<'a, 'b> {
|
||||||
|
fn accepts(active_block: ActiveLidarrBlock) -> bool {
|
||||||
|
SYSTEM_DETAILS_BLOCKS.contains(&active_block)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new(
|
||||||
|
key: Key,
|
||||||
|
app: &'a mut App<'b>,
|
||||||
|
active_block: ActiveLidarrBlock,
|
||||||
|
context: Option<ActiveLidarrBlock>,
|
||||||
|
) -> SystemDetailsHandler<'a, 'b> {
|
||||||
|
SystemDetailsHandler {
|
||||||
|
key,
|
||||||
|
app,
|
||||||
|
active_lidarr_block: active_block,
|
||||||
|
_context: context,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_key(&self) -> Key {
|
||||||
|
self.key
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ignore_special_keys(&self) -> bool {
|
||||||
|
self.app.ignore_special_keys_for_textbox_input
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_ready(&self) -> bool {
|
||||||
|
!self.app.is_loading
|
||||||
|
&& (!self.app.data.lidarr_data.log_details.is_empty()
|
||||||
|
|| !self.app.data.lidarr_data.tasks.is_empty()
|
||||||
|
|| !self.app.data.lidarr_data.updates.is_empty())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_scroll_up(&mut self) {
|
||||||
|
match self.active_lidarr_block {
|
||||||
|
ActiveLidarrBlock::SystemLogs => self.app.data.lidarr_data.log_details.scroll_up(),
|
||||||
|
ActiveLidarrBlock::SystemTasks => self.app.data.lidarr_data.tasks.scroll_up(),
|
||||||
|
ActiveLidarrBlock::SystemUpdates => self.app.data.lidarr_data.updates.scroll_up(),
|
||||||
|
ActiveLidarrBlock::SystemQueuedEvents => self.app.data.lidarr_data.queued_events.scroll_up(),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_scroll_down(&mut self) {
|
||||||
|
match self.active_lidarr_block {
|
||||||
|
ActiveLidarrBlock::SystemLogs => self.app.data.lidarr_data.log_details.scroll_down(),
|
||||||
|
ActiveLidarrBlock::SystemTasks => self.app.data.lidarr_data.tasks.scroll_down(),
|
||||||
|
ActiveLidarrBlock::SystemUpdates => self.app.data.lidarr_data.updates.scroll_down(),
|
||||||
|
ActiveLidarrBlock::SystemQueuedEvents => {
|
||||||
|
self.app.data.lidarr_data.queued_events.scroll_down()
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_home(&mut self) {
|
||||||
|
match self.active_lidarr_block {
|
||||||
|
ActiveLidarrBlock::SystemLogs => self.app.data.lidarr_data.log_details.scroll_to_top(),
|
||||||
|
ActiveLidarrBlock::SystemTasks => self.app.data.lidarr_data.tasks.scroll_to_top(),
|
||||||
|
ActiveLidarrBlock::SystemUpdates => self.app.data.lidarr_data.updates.scroll_to_top(),
|
||||||
|
ActiveLidarrBlock::SystemQueuedEvents => {
|
||||||
|
self.app.data.lidarr_data.queued_events.scroll_to_top()
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_end(&mut self) {
|
||||||
|
match self.active_lidarr_block {
|
||||||
|
ActiveLidarrBlock::SystemLogs => self.app.data.lidarr_data.log_details.scroll_to_bottom(),
|
||||||
|
ActiveLidarrBlock::SystemTasks => self.app.data.lidarr_data.tasks.scroll_to_bottom(),
|
||||||
|
ActiveLidarrBlock::SystemUpdates => self.app.data.lidarr_data.updates.scroll_to_bottom(),
|
||||||
|
ActiveLidarrBlock::SystemQueuedEvents => {
|
||||||
|
self.app.data.lidarr_data.queued_events.scroll_to_bottom()
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_delete(&mut self) {}
|
||||||
|
|
||||||
|
fn handle_left_right_action(&mut self) {
|
||||||
|
let key = self.key;
|
||||||
|
|
||||||
|
match self.active_lidarr_block {
|
||||||
|
ActiveLidarrBlock::SystemLogs => match self.key {
|
||||||
|
_ if matches_key!(left, key) => {
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.log_details
|
||||||
|
.items
|
||||||
|
.iter()
|
||||||
|
.for_each(|log| log.scroll_right());
|
||||||
|
}
|
||||||
|
_ if matches_key!(right, key) => {
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.log_details
|
||||||
|
.items
|
||||||
|
.iter()
|
||||||
|
.for_each(|log| log.scroll_left());
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
},
|
||||||
|
ActiveLidarrBlock::SystemTaskStartConfirmPrompt => handle_prompt_toggle(self.app, self.key),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_submit(&mut self) {
|
||||||
|
match self.active_lidarr_block {
|
||||||
|
ActiveLidarrBlock::SystemTasks => {
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.push_navigation_stack(ActiveLidarrBlock::SystemTaskStartConfirmPrompt.into());
|
||||||
|
}
|
||||||
|
ActiveLidarrBlock::SystemTaskStartConfirmPrompt => {
|
||||||
|
if self.app.data.lidarr_data.prompt_confirm {
|
||||||
|
self.app.data.lidarr_data.prompt_confirm_action =
|
||||||
|
Some(LidarrEvent::StartTask(self.extract_task_name()));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.app.pop_navigation_stack();
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_esc(&mut self) {
|
||||||
|
match self.active_lidarr_block {
|
||||||
|
ActiveLidarrBlock::SystemLogs => {
|
||||||
|
self.app.data.lidarr_data.log_details = StatefulList::default();
|
||||||
|
self.app.pop_navigation_stack()
|
||||||
|
}
|
||||||
|
ActiveLidarrBlock::SystemQueuedEvents
|
||||||
|
| ActiveLidarrBlock::SystemTasks
|
||||||
|
| ActiveLidarrBlock::SystemUpdates => self.app.pop_navigation_stack(),
|
||||||
|
ActiveLidarrBlock::SystemTaskStartConfirmPrompt => {
|
||||||
|
self.app.pop_navigation_stack();
|
||||||
|
self.app.data.lidarr_data.prompt_confirm = false;
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_char_key_event(&mut self) {
|
||||||
|
if SYSTEM_DETAILS_BLOCKS.contains(&self.active_lidarr_block) && matches_key!(refresh, self.key)
|
||||||
|
{
|
||||||
|
self.app.should_refresh = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.active_lidarr_block == ActiveLidarrBlock::SystemTaskStartConfirmPrompt
|
||||||
|
&& matches_key!(confirm, self.key)
|
||||||
|
{
|
||||||
|
self.app.data.lidarr_data.prompt_confirm = true;
|
||||||
|
self.app.data.lidarr_data.prompt_confirm_action =
|
||||||
|
Some(LidarrEvent::StartTask(self.extract_task_name()));
|
||||||
|
self.app.pop_navigation_stack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn app_mut(&mut self) -> &mut App<'b> {
|
||||||
|
self.app
|
||||||
|
}
|
||||||
|
|
||||||
|
fn current_route(&self) -> Route {
|
||||||
|
self.app.get_current_route()
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,580 @@
|
|||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use rstest::rstest;
|
||||||
|
use strum::IntoEnumIterator;
|
||||||
|
|
||||||
|
use crate::app::App;
|
||||||
|
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
||||||
|
use crate::assert_navigation_pushed;
|
||||||
|
use crate::event::Key;
|
||||||
|
use crate::handlers::KeyEventHandler;
|
||||||
|
use crate::handlers::lidarr_handlers::system::SystemHandler;
|
||||||
|
use crate::models::lidarr_models::LidarrTask;
|
||||||
|
use crate::models::servarr_data::lidarr::lidarr_data::{
|
||||||
|
ActiveLidarrBlock, SYSTEM_DETAILS_BLOCKS,
|
||||||
|
};
|
||||||
|
use crate::models::servarr_models::QueueEvent;
|
||||||
|
use crate::test_handler_delegation;
|
||||||
|
|
||||||
|
mod test_handle_left_right_action {
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use crate::assert_navigation_pushed;
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn test_system_tab_left(#[values(true, false)] is_ready: bool) {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::System.into());
|
||||||
|
app.is_loading = is_ready;
|
||||||
|
app.data.lidarr_data.main_tabs.set_index(5);
|
||||||
|
|
||||||
|
SystemHandler::new(
|
||||||
|
DEFAULT_KEYBINDINGS.left.key,
|
||||||
|
&mut app,
|
||||||
|
ActiveLidarrBlock::System,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.handle();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
app.data.lidarr_data.main_tabs.get_active_route(),
|
||||||
|
ActiveLidarrBlock::Indexers.into()
|
||||||
|
);
|
||||||
|
assert_navigation_pushed!(app, ActiveLidarrBlock::Indexers.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn test_system_tab_right(#[values(true, false)] is_ready: bool) {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::System.into());
|
||||||
|
app.is_loading = is_ready;
|
||||||
|
app.data.lidarr_data.main_tabs.set_index(5);
|
||||||
|
|
||||||
|
SystemHandler::new(
|
||||||
|
DEFAULT_KEYBINDINGS.right.key,
|
||||||
|
&mut app,
|
||||||
|
ActiveLidarrBlock::System,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.handle();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
app.data.lidarr_data.main_tabs.get_active_route(),
|
||||||
|
ActiveLidarrBlock::Artists.into()
|
||||||
|
);
|
||||||
|
assert_navigation_pushed!(app, ActiveLidarrBlock::Artists.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod test_handle_esc {
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use crate::assert_navigation_popped;
|
||||||
|
|
||||||
|
const ESC_KEY: Key = DEFAULT_KEYBINDINGS.esc.key;
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn test_default_esc(#[values(true, false)] is_loading: bool) {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.is_loading = is_loading;
|
||||||
|
app.error = "test error".to_owned().into();
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::System.into());
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::System.into());
|
||||||
|
|
||||||
|
SystemHandler::new(ESC_KEY, &mut app, ActiveLidarrBlock::System, None).handle();
|
||||||
|
|
||||||
|
assert_navigation_popped!(app, ActiveLidarrBlock::System.into());
|
||||||
|
assert_is_empty!(app.error.text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod test_handle_key_char {
|
||||||
|
use pretty_assertions::{assert_eq, assert_str_eq};
|
||||||
|
|
||||||
|
use crate::models::HorizontallyScrollableText;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_update_system_key() {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::System.into());
|
||||||
|
app.data.lidarr_data.logs.set_items(vec![
|
||||||
|
HorizontallyScrollableText::from("test 1"),
|
||||||
|
HorizontallyScrollableText::from("test 2"),
|
||||||
|
]);
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.queued_events
|
||||||
|
.set_items(vec![QueueEvent::default()]);
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.tasks
|
||||||
|
.set_items(vec![LidarrTask::default()]);
|
||||||
|
|
||||||
|
SystemHandler::new(
|
||||||
|
DEFAULT_KEYBINDINGS.update.key,
|
||||||
|
&mut app,
|
||||||
|
ActiveLidarrBlock::System,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.handle();
|
||||||
|
|
||||||
|
assert_navigation_pushed!(app, ActiveLidarrBlock::SystemUpdates.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_update_system_key_no_op_if_not_ready() {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.is_loading = true;
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::System.into());
|
||||||
|
app.data.lidarr_data.logs.set_items(vec![
|
||||||
|
HorizontallyScrollableText::from("test 1"),
|
||||||
|
HorizontallyScrollableText::from("test 2"),
|
||||||
|
]);
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.queued_events
|
||||||
|
.set_items(vec![QueueEvent::default()]);
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.tasks
|
||||||
|
.set_items(vec![LidarrTask::default()]);
|
||||||
|
|
||||||
|
SystemHandler::new(
|
||||||
|
DEFAULT_KEYBINDINGS.update.key,
|
||||||
|
&mut app,
|
||||||
|
ActiveLidarrBlock::System,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.handle();
|
||||||
|
|
||||||
|
assert_eq!(app.get_current_route(), ActiveLidarrBlock::System.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_queued_events_key() {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::System.into());
|
||||||
|
app.data.lidarr_data.logs.set_items(vec![
|
||||||
|
HorizontallyScrollableText::from("test 1"),
|
||||||
|
HorizontallyScrollableText::from("test 2"),
|
||||||
|
]);
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.queued_events
|
||||||
|
.set_items(vec![QueueEvent::default()]);
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.tasks
|
||||||
|
.set_items(vec![LidarrTask::default()]);
|
||||||
|
|
||||||
|
SystemHandler::new(
|
||||||
|
DEFAULT_KEYBINDINGS.events.key,
|
||||||
|
&mut app,
|
||||||
|
ActiveLidarrBlock::System,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.handle();
|
||||||
|
|
||||||
|
assert_navigation_pushed!(app, ActiveLidarrBlock::SystemQueuedEvents.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_queued_events_key_no_op_if_not_ready() {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.is_loading = true;
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::System.into());
|
||||||
|
app.data.lidarr_data.logs.set_items(vec![
|
||||||
|
HorizontallyScrollableText::from("test 1"),
|
||||||
|
HorizontallyScrollableText::from("test 2"),
|
||||||
|
]);
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.queued_events
|
||||||
|
.set_items(vec![QueueEvent::default()]);
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.tasks
|
||||||
|
.set_items(vec![LidarrTask::default()]);
|
||||||
|
|
||||||
|
SystemHandler::new(
|
||||||
|
DEFAULT_KEYBINDINGS.events.key,
|
||||||
|
&mut app,
|
||||||
|
ActiveLidarrBlock::System,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.handle();
|
||||||
|
|
||||||
|
assert_eq!(app.get_current_route(), ActiveLidarrBlock::System.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_refresh_system_key() {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.data.lidarr_data.logs.set_items(vec![
|
||||||
|
HorizontallyScrollableText::from("test 1"),
|
||||||
|
HorizontallyScrollableText::from("test 2"),
|
||||||
|
]);
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.queued_events
|
||||||
|
.set_items(vec![QueueEvent::default()]);
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.tasks
|
||||||
|
.set_items(vec![LidarrTask::default()]);
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::System.into());
|
||||||
|
|
||||||
|
SystemHandler::new(
|
||||||
|
DEFAULT_KEYBINDINGS.refresh.key,
|
||||||
|
&mut app,
|
||||||
|
ActiveLidarrBlock::System,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.handle();
|
||||||
|
|
||||||
|
assert_navigation_pushed!(app, ActiveLidarrBlock::System.into());
|
||||||
|
assert!(app.should_refresh);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_refresh_system_key_no_op_if_not_ready() {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.is_loading = true;
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::System.into());
|
||||||
|
app.data.lidarr_data.logs.set_items(vec![
|
||||||
|
HorizontallyScrollableText::from("test 1"),
|
||||||
|
HorizontallyScrollableText::from("test 2"),
|
||||||
|
]);
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.queued_events
|
||||||
|
.set_items(vec![QueueEvent::default()]);
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.tasks
|
||||||
|
.set_items(vec![LidarrTask::default()]);
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::System.into());
|
||||||
|
|
||||||
|
SystemHandler::new(
|
||||||
|
DEFAULT_KEYBINDINGS.refresh.key,
|
||||||
|
&mut app,
|
||||||
|
ActiveLidarrBlock::System,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.handle();
|
||||||
|
|
||||||
|
assert_eq!(app.get_current_route(), ActiveLidarrBlock::System.into());
|
||||||
|
assert!(!app.should_refresh);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_logs_key() {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::System.into());
|
||||||
|
app.data.lidarr_data.logs.set_items(vec![
|
||||||
|
HorizontallyScrollableText::from("test 1"),
|
||||||
|
HorizontallyScrollableText::from("test 2"),
|
||||||
|
]);
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.queued_events
|
||||||
|
.set_items(vec![QueueEvent::default()]);
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.tasks
|
||||||
|
.set_items(vec![LidarrTask::default()]);
|
||||||
|
|
||||||
|
SystemHandler::new(
|
||||||
|
DEFAULT_KEYBINDINGS.logs.key,
|
||||||
|
&mut app,
|
||||||
|
ActiveLidarrBlock::System,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.handle();
|
||||||
|
|
||||||
|
assert_navigation_pushed!(app, ActiveLidarrBlock::SystemLogs.into());
|
||||||
|
assert_eq!(
|
||||||
|
app.data.lidarr_data.log_details.items,
|
||||||
|
app.data.lidarr_data.logs.items
|
||||||
|
);
|
||||||
|
assert_str_eq!(
|
||||||
|
app.data.lidarr_data.log_details.current_selection().text,
|
||||||
|
"test 2"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_logs_key_no_op_when_not_ready() {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.is_loading = true;
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::System.into());
|
||||||
|
app.data.lidarr_data.logs.set_items(vec![
|
||||||
|
HorizontallyScrollableText::from("test 1"),
|
||||||
|
HorizontallyScrollableText::from("test 2"),
|
||||||
|
]);
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.queued_events
|
||||||
|
.set_items(vec![QueueEvent::default()]);
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.tasks
|
||||||
|
.set_items(vec![LidarrTask::default()]);
|
||||||
|
|
||||||
|
SystemHandler::new(
|
||||||
|
DEFAULT_KEYBINDINGS.logs.key,
|
||||||
|
&mut app,
|
||||||
|
ActiveLidarrBlock::System,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.handle();
|
||||||
|
|
||||||
|
assert_eq!(app.get_current_route(), ActiveLidarrBlock::System.into());
|
||||||
|
assert_is_empty!(app.data.lidarr_data.log_details);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tasks_key() {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::System.into());
|
||||||
|
app.data.lidarr_data.logs.set_items(vec![
|
||||||
|
HorizontallyScrollableText::from("test 1"),
|
||||||
|
HorizontallyScrollableText::from("test 2"),
|
||||||
|
]);
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.queued_events
|
||||||
|
.set_items(vec![QueueEvent::default()]);
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.tasks
|
||||||
|
.set_items(vec![LidarrTask::default()]);
|
||||||
|
|
||||||
|
SystemHandler::new(
|
||||||
|
DEFAULT_KEYBINDINGS.tasks.key,
|
||||||
|
&mut app,
|
||||||
|
ActiveLidarrBlock::System,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.handle();
|
||||||
|
|
||||||
|
assert_navigation_pushed!(app, ActiveLidarrBlock::SystemTasks.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tasks_key_no_op_when_not_ready() {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.is_loading = true;
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::System.into());
|
||||||
|
app.data.lidarr_data.logs.set_items(vec![
|
||||||
|
HorizontallyScrollableText::from("test 1"),
|
||||||
|
HorizontallyScrollableText::from("test 2"),
|
||||||
|
]);
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.queued_events
|
||||||
|
.set_items(vec![QueueEvent::default()]);
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.tasks
|
||||||
|
.set_items(vec![LidarrTask::default()]);
|
||||||
|
|
||||||
|
SystemHandler::new(
|
||||||
|
DEFAULT_KEYBINDINGS.tasks.key,
|
||||||
|
&mut app,
|
||||||
|
ActiveLidarrBlock::System,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.handle();
|
||||||
|
|
||||||
|
assert_eq!(app.get_current_route(), ActiveLidarrBlock::System.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn test_delegates_system_details_blocks_to_system_details_handler(
|
||||||
|
#[values(
|
||||||
|
ActiveLidarrBlock::SystemLogs,
|
||||||
|
ActiveLidarrBlock::SystemQueuedEvents,
|
||||||
|
ActiveLidarrBlock::SystemTasks,
|
||||||
|
ActiveLidarrBlock::SystemTaskStartConfirmPrompt,
|
||||||
|
ActiveLidarrBlock::SystemUpdates
|
||||||
|
)]
|
||||||
|
active_lidarr_block: ActiveLidarrBlock,
|
||||||
|
) {
|
||||||
|
test_handler_delegation!(
|
||||||
|
SystemHandler,
|
||||||
|
ActiveLidarrBlock::System,
|
||||||
|
active_lidarr_block
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_system_handler_accepts() {
|
||||||
|
let mut system_blocks = vec![ActiveLidarrBlock::System];
|
||||||
|
system_blocks.extend(SYSTEM_DETAILS_BLOCKS);
|
||||||
|
|
||||||
|
ActiveLidarrBlock::iter().for_each(|active_lidarr_block| {
|
||||||
|
if system_blocks.contains(&active_lidarr_block) {
|
||||||
|
assert!(SystemHandler::accepts(active_lidarr_block));
|
||||||
|
} else {
|
||||||
|
assert!(!SystemHandler::accepts(active_lidarr_block));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn test_system_handler_ignore_special_keys(
|
||||||
|
#[values(true, false)] ignore_special_keys_for_textbox_input: bool,
|
||||||
|
) {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.ignore_special_keys_for_textbox_input = ignore_special_keys_for_textbox_input;
|
||||||
|
let handler = SystemHandler::new(
|
||||||
|
DEFAULT_KEYBINDINGS.esc.key,
|
||||||
|
&mut app,
|
||||||
|
ActiveLidarrBlock::default(),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
handler.ignore_special_keys(),
|
||||||
|
ignore_special_keys_for_textbox_input
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_system_handler_is_not_ready_when_loading() {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::System.into());
|
||||||
|
app.is_loading = true;
|
||||||
|
|
||||||
|
let system_handler = SystemHandler::new(
|
||||||
|
DEFAULT_KEYBINDINGS.update.key,
|
||||||
|
&mut app,
|
||||||
|
ActiveLidarrBlock::System,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(!system_handler.is_ready());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_system_handler_is_not_ready_when_logs_is_empty() {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::System.into());
|
||||||
|
app.is_loading = false;
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.tasks
|
||||||
|
.set_items(vec![LidarrTask::default()]);
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.queued_events
|
||||||
|
.set_items(vec![QueueEvent::default()]);
|
||||||
|
|
||||||
|
let system_handler = SystemHandler::new(
|
||||||
|
DEFAULT_KEYBINDINGS.update.key,
|
||||||
|
&mut app,
|
||||||
|
ActiveLidarrBlock::System,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(!system_handler.is_ready());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_system_handler_is_not_ready_when_tasks_is_empty() {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::System.into());
|
||||||
|
app.is_loading = false;
|
||||||
|
app.data.lidarr_data.logs.set_items(vec!["test".into()]);
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.queued_events
|
||||||
|
.set_items(vec![QueueEvent::default()]);
|
||||||
|
|
||||||
|
let system_handler = SystemHandler::new(
|
||||||
|
DEFAULT_KEYBINDINGS.update.key,
|
||||||
|
&mut app,
|
||||||
|
ActiveLidarrBlock::System,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(!system_handler.is_ready());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_system_handler_is_not_ready_when_queued_events_is_empty() {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::System.into());
|
||||||
|
app.is_loading = false;
|
||||||
|
app.data.lidarr_data.logs.set_items(vec!["test".into()]);
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.tasks
|
||||||
|
.set_items(vec![LidarrTask::default()]);
|
||||||
|
|
||||||
|
let system_handler = SystemHandler::new(
|
||||||
|
DEFAULT_KEYBINDINGS.update.key,
|
||||||
|
&mut app,
|
||||||
|
ActiveLidarrBlock::System,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(!system_handler.is_ready());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_system_handler_is_ready_when_all_required_tables_are_not_empty() {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::System.into());
|
||||||
|
app.is_loading = false;
|
||||||
|
app.data.lidarr_data.logs.set_items(vec!["test".into()]);
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.tasks
|
||||||
|
.set_items(vec![LidarrTask::default()]);
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.queued_events
|
||||||
|
.set_items(vec![QueueEvent::default()]);
|
||||||
|
|
||||||
|
let system_handler = SystemHandler::new(
|
||||||
|
DEFAULT_KEYBINDINGS.update.key,
|
||||||
|
&mut app,
|
||||||
|
ActiveLidarrBlock::System,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(system_handler.is_ready());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,13 +5,15 @@ use super::{
|
|||||||
SecurityConfig, Tag,
|
SecurityConfig, Tag,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use crate::models::servarr_models::IndexerSettings;
|
use crate::models::servarr_models::{IndexerSettings, LogResponse, QueueEvent, Update};
|
||||||
use crate::serde_enum_from;
|
use crate::serde_enum_from;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
|
use clap::ValueEnum;
|
||||||
use derivative::Derivative;
|
use derivative::Derivative;
|
||||||
use enum_display_style_derive::EnumDisplayStyle;
|
use enum_display_style_derive::EnumDisplayStyle;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::{Number, Value};
|
use serde_json::{Number, Value};
|
||||||
|
use std::fmt::{Display, Formatter};
|
||||||
use strum::{Display, EnumIter};
|
use strum::{Display, EnumIter};
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -426,6 +428,42 @@ pub struct LidarrHistoryItem {
|
|||||||
pub data: LidarrHistoryData,
|
pub data: LidarrHistoryData,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct LidarrTask {
|
||||||
|
pub name: String,
|
||||||
|
pub task_name: LidarrTaskName,
|
||||||
|
#[serde(deserialize_with = "super::from_i64")]
|
||||||
|
pub interval: i64,
|
||||||
|
pub last_execution: DateTime<Utc>,
|
||||||
|
pub next_execution: DateTime<Utc>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Copy, ValueEnum)]
|
||||||
|
#[serde(rename_all = "PascalCase")]
|
||||||
|
pub enum LidarrTaskName {
|
||||||
|
#[default]
|
||||||
|
ApplicationUpdateCheck,
|
||||||
|
Backup,
|
||||||
|
CheckHealth,
|
||||||
|
Housekeeping,
|
||||||
|
ImportListSync,
|
||||||
|
MessagingCleanup,
|
||||||
|
RefreshArtist,
|
||||||
|
RefreshMonitoredDownloads,
|
||||||
|
RescanFolders,
|
||||||
|
RssSync,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for LidarrTaskName {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let task_name = serde_json::to_string(&self)
|
||||||
|
.expect("Unable to serialize task name")
|
||||||
|
.replace('"', "");
|
||||||
|
write!(f, "{task_name}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<LidarrSerdeable> for Serdeable {
|
impl From<LidarrSerdeable> for Serdeable {
|
||||||
fn from(value: LidarrSerdeable) -> Serdeable {
|
fn from(value: LidarrSerdeable) -> Serdeable {
|
||||||
Serdeable::Lidarr(value)
|
Serdeable::Lidarr(value)
|
||||||
@@ -446,13 +484,17 @@ serde_enum_from!(
|
|||||||
IndexerSettings(IndexerSettings),
|
IndexerSettings(IndexerSettings),
|
||||||
Indexers(Vec<Indexer>),
|
Indexers(Vec<Indexer>),
|
||||||
IndexerTestResults(Vec<IndexerTestResult>),
|
IndexerTestResults(Vec<IndexerTestResult>),
|
||||||
|
LogResponse(LogResponse),
|
||||||
MetadataProfiles(Vec<MetadataProfile>),
|
MetadataProfiles(Vec<MetadataProfile>),
|
||||||
QualityProfiles(Vec<QualityProfile>),
|
QualityProfiles(Vec<QualityProfile>),
|
||||||
|
QueueEvents(Vec<QueueEvent>),
|
||||||
RootFolders(Vec<RootFolder>),
|
RootFolders(Vec<RootFolder>),
|
||||||
SecurityConfig(SecurityConfig),
|
SecurityConfig(SecurityConfig),
|
||||||
SystemStatus(SystemStatus),
|
SystemStatus(SystemStatus),
|
||||||
Tag(Tag),
|
Tag(Tag),
|
||||||
Tags(Vec<Tag>),
|
Tags(Vec<Tag>),
|
||||||
|
Tasks(Vec<LidarrTask>),
|
||||||
|
Updates(Vec<Update>),
|
||||||
Value(Value),
|
Value(Value),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -6,12 +6,12 @@ mod tests {
|
|||||||
|
|
||||||
use crate::models::lidarr_models::{
|
use crate::models::lidarr_models::{
|
||||||
AddArtistSearchResult, Album, DownloadRecord, DownloadStatus, DownloadsResponse,
|
AddArtistSearchResult, Album, DownloadRecord, DownloadStatus, DownloadsResponse,
|
||||||
LidarrHistoryEventType, LidarrHistoryItem, LidarrHistoryWrapper, Member, MetadataProfile,
|
LidarrHistoryEventType, LidarrHistoryItem, LidarrHistoryWrapper, LidarrTask, Member,
|
||||||
MonitorType, NewItemMonitorType, SystemStatus,
|
MetadataProfile, MonitorType, NewItemMonitorType, SystemStatus,
|
||||||
};
|
};
|
||||||
use crate::models::servarr_models::{
|
use crate::models::servarr_models::{
|
||||||
DiskSpace, HostConfig, Indexer, IndexerSettings, IndexerTestResult, QualityProfile, RootFolder,
|
DiskSpace, HostConfig, Indexer, IndexerSettings, IndexerTestResult, Log, LogResponse,
|
||||||
SecurityConfig, Tag,
|
QualityProfile, QueueEvent, RootFolder, SecurityConfig, Tag, Update,
|
||||||
};
|
};
|
||||||
use crate::models::{
|
use crate::models::{
|
||||||
Serdeable,
|
Serdeable,
|
||||||
@@ -363,6 +363,20 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lidarr_serdeable_from_log_response() {
|
||||||
|
let log_response = LogResponse {
|
||||||
|
records: vec![Log {
|
||||||
|
level: "info".to_owned(),
|
||||||
|
..Log::default()
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
|
||||||
|
let lidarr_serdeable: LidarrSerdeable = log_response.clone().into();
|
||||||
|
|
||||||
|
assert_eq!(lidarr_serdeable, LidarrSerdeable::LogResponse(log_response));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_lidarr_serdeable_from_metadata_profiles() {
|
fn test_lidarr_serdeable_from_metadata_profiles() {
|
||||||
let metadata_profiles = vec![MetadataProfile {
|
let metadata_profiles = vec![MetadataProfile {
|
||||||
@@ -405,6 +419,18 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lidarr_serdeable_from_queue_events() {
|
||||||
|
let queue_events = vec![QueueEvent {
|
||||||
|
trigger: "test".to_owned(),
|
||||||
|
..QueueEvent::default()
|
||||||
|
}];
|
||||||
|
|
||||||
|
let lidarr_serdeable: LidarrSerdeable = queue_events.clone().into();
|
||||||
|
|
||||||
|
assert_eq!(lidarr_serdeable, LidarrSerdeable::QueueEvents(queue_events));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_lidarr_serdeable_from_root_folders() {
|
fn test_lidarr_serdeable_from_root_folders() {
|
||||||
let root_folders = vec![RootFolder {
|
let root_folders = vec![RootFolder {
|
||||||
@@ -501,6 +527,30 @@ mod tests {
|
|||||||
assert_eq!(lidarr_serdeable, LidarrSerdeable::Album(album));
|
assert_eq!(lidarr_serdeable, LidarrSerdeable::Album(album));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lidarr_serdeable_from_tasks() {
|
||||||
|
let tasks = vec![LidarrTask {
|
||||||
|
name: "test".to_owned(),
|
||||||
|
..LidarrTask::default()
|
||||||
|
}];
|
||||||
|
|
||||||
|
let lidarr_serdeable: LidarrSerdeable = tasks.clone().into();
|
||||||
|
|
||||||
|
assert_eq!(lidarr_serdeable, LidarrSerdeable::Tasks(tasks));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lidarr_serdeable_from_updates() {
|
||||||
|
let updates = vec![Update {
|
||||||
|
version: "test".to_owned(),
|
||||||
|
..Update::default()
|
||||||
|
}];
|
||||||
|
|
||||||
|
let lidarr_serdeable: LidarrSerdeable = updates.clone().into();
|
||||||
|
|
||||||
|
assert_eq!(lidarr_serdeable, LidarrSerdeable::Updates(updates));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_artist_status_display() {
|
fn test_artist_status_display() {
|
||||||
assert_str_eq!(ArtistStatus::Continuing.to_string(), "continuing");
|
assert_str_eq!(ArtistStatus::Continuing.to_string(), "continuing");
|
||||||
|
|||||||
@@ -3,15 +3,17 @@ use serde_json::Number;
|
|||||||
use super::modals::{AddArtistModal, AddRootFolderModal, EditArtistModal};
|
use super::modals::{AddArtistModal, AddRootFolderModal, EditArtistModal};
|
||||||
use crate::app::context_clues::{
|
use crate::app::context_clues::{
|
||||||
DOWNLOADS_CONTEXT_CLUES, HISTORY_CONTEXT_CLUES, INDEXERS_CONTEXT_CLUES,
|
DOWNLOADS_CONTEXT_CLUES, HISTORY_CONTEXT_CLUES, INDEXERS_CONTEXT_CLUES,
|
||||||
ROOT_FOLDERS_CONTEXT_CLUES,
|
ROOT_FOLDERS_CONTEXT_CLUES, SYSTEM_CONTEXT_CLUES,
|
||||||
};
|
};
|
||||||
use crate::app::lidarr::lidarr_context_clues::{
|
use crate::app::lidarr::lidarr_context_clues::{
|
||||||
ARTIST_DETAILS_CONTEXT_CLUES, ARTISTS_CONTEXT_CLUES,
|
ARTIST_DETAILS_CONTEXT_CLUES, ARTISTS_CONTEXT_CLUES,
|
||||||
};
|
};
|
||||||
|
use crate::models::lidarr_models::LidarrTask;
|
||||||
use crate::models::servarr_data::modals::EditIndexerModal;
|
use crate::models::servarr_data::modals::EditIndexerModal;
|
||||||
use crate::models::servarr_models::IndexerSettings;
|
use crate::models::servarr_models::{IndexerSettings, QueueEvent};
|
||||||
|
use crate::models::stateful_list::StatefulList;
|
||||||
use crate::models::{
|
use crate::models::{
|
||||||
BlockSelectionState, HorizontallyScrollableText, Route, TabRoute, TabState,
|
BlockSelectionState, HorizontallyScrollableText, Route, ScrollableText, TabRoute, TabState,
|
||||||
lidarr_models::{AddArtistSearchResult, Album, Artist, DownloadRecord, LidarrHistoryItem},
|
lidarr_models::{AddArtistSearchResult, Album, Artist, DownloadRecord, LidarrHistoryItem},
|
||||||
servarr_data::modals::IndexerTestResultModalItem,
|
servarr_data::modals::IndexerTestResultModalItem,
|
||||||
servarr_models::{DiskSpace, Indexer, RootFolder},
|
servarr_models::{DiskSpace, Indexer, RootFolder},
|
||||||
@@ -32,8 +34,11 @@ use {
|
|||||||
add_artist_search_result, album, artist, download_record, indexer, lidarr_history_item,
|
add_artist_search_result, album, artist, download_record, indexer, lidarr_history_item,
|
||||||
metadata_profile, metadata_profile_map, quality_profile, root_folder, tags_map,
|
metadata_profile, metadata_profile_map, quality_profile, root_folder, tags_map,
|
||||||
},
|
},
|
||||||
|
crate::network::lidarr_network::lidarr_network_test_utils::test_utils::{log_line, task},
|
||||||
crate::network::servarr_test_utils::diskspace,
|
crate::network::servarr_test_utils::diskspace,
|
||||||
crate::network::servarr_test_utils::indexer_test_result,
|
crate::network::servarr_test_utils::indexer_test_result,
|
||||||
|
crate::network::servarr_test_utils::queued_event,
|
||||||
|
crate::network::sonarr_network::sonarr_network_test_utils::test_utils::updates,
|
||||||
strum::{Display, EnumString, IntoEnumIterator},
|
strum::{Display, EnumString, IntoEnumIterator},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -60,15 +65,20 @@ pub struct LidarrData<'a> {
|
|||||||
pub indexer_settings: Option<IndexerSettings>,
|
pub indexer_settings: Option<IndexerSettings>,
|
||||||
pub indexer_test_all_results: Option<StatefulTable<IndexerTestResultModalItem>>,
|
pub indexer_test_all_results: Option<StatefulTable<IndexerTestResultModalItem>>,
|
||||||
pub indexer_test_errors: Option<String>,
|
pub indexer_test_errors: Option<String>,
|
||||||
|
pub logs: StatefulList<HorizontallyScrollableText>,
|
||||||
|
pub log_details: StatefulList<HorizontallyScrollableText>,
|
||||||
pub main_tabs: TabState,
|
pub main_tabs: TabState,
|
||||||
pub metadata_profile_map: BiMap<i64, String>,
|
pub metadata_profile_map: BiMap<i64, String>,
|
||||||
pub prompt_confirm: bool,
|
pub prompt_confirm: bool,
|
||||||
pub prompt_confirm_action: Option<LidarrEvent>,
|
pub prompt_confirm_action: Option<LidarrEvent>,
|
||||||
pub quality_profile_map: BiMap<i64, String>,
|
pub quality_profile_map: BiMap<i64, String>,
|
||||||
|
pub queued_events: StatefulTable<QueueEvent>,
|
||||||
pub root_folders: StatefulTable<RootFolder>,
|
pub root_folders: StatefulTable<RootFolder>,
|
||||||
pub selected_block: BlockSelectionState<'a, ActiveLidarrBlock>,
|
pub selected_block: BlockSelectionState<'a, ActiveLidarrBlock>,
|
||||||
pub start_time: DateTime<Utc>,
|
pub start_time: DateTime<Utc>,
|
||||||
pub tags_map: BiMap<i64, String>,
|
pub tags_map: BiMap<i64, String>,
|
||||||
|
pub tasks: StatefulTable<LidarrTask>,
|
||||||
|
pub updates: ScrollableText,
|
||||||
pub version: String,
|
pub version: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,14 +145,19 @@ impl<'a> Default for LidarrData<'a> {
|
|||||||
indexer_settings: None,
|
indexer_settings: None,
|
||||||
indexer_test_all_results: None,
|
indexer_test_all_results: None,
|
||||||
indexer_test_errors: None,
|
indexer_test_errors: None,
|
||||||
|
logs: StatefulList::default(),
|
||||||
|
log_details: StatefulList::default(),
|
||||||
metadata_profile_map: BiMap::new(),
|
metadata_profile_map: BiMap::new(),
|
||||||
prompt_confirm: false,
|
prompt_confirm: false,
|
||||||
prompt_confirm_action: None,
|
prompt_confirm_action: None,
|
||||||
quality_profile_map: BiMap::new(),
|
quality_profile_map: BiMap::new(),
|
||||||
|
queued_events: StatefulTable::default(),
|
||||||
root_folders: StatefulTable::default(),
|
root_folders: StatefulTable::default(),
|
||||||
selected_block: BlockSelectionState::default(),
|
selected_block: BlockSelectionState::default(),
|
||||||
start_time: DateTime::default(),
|
start_time: DateTime::default(),
|
||||||
tags_map: BiMap::new(),
|
tags_map: BiMap::new(),
|
||||||
|
tasks: StatefulTable::default(),
|
||||||
|
updates: ScrollableText::default(),
|
||||||
version: String::new(),
|
version: String::new(),
|
||||||
main_tabs: TabState::new(vec![
|
main_tabs: TabState::new(vec![
|
||||||
TabRoute {
|
TabRoute {
|
||||||
@@ -175,6 +190,12 @@ impl<'a> Default for LidarrData<'a> {
|
|||||||
contextual_help: Some(&INDEXERS_CONTEXT_CLUES),
|
contextual_help: Some(&INDEXERS_CONTEXT_CLUES),
|
||||||
config: None,
|
config: None,
|
||||||
},
|
},
|
||||||
|
TabRoute {
|
||||||
|
title: "System".to_string(),
|
||||||
|
route: ActiveLidarrBlock::System.into(),
|
||||||
|
contextual_help: Some(&SYSTEM_CONTEXT_CLUES),
|
||||||
|
config: None,
|
||||||
|
},
|
||||||
]),
|
]),
|
||||||
artist_info_tabs: TabState::new(vec![TabRoute {
|
artist_info_tabs: TabState::new(vec![TabRoute {
|
||||||
title: "Albums".to_string(),
|
title: "Albums".to_string(),
|
||||||
@@ -270,7 +291,10 @@ impl LidarrData<'_> {
|
|||||||
indexer_settings: Some(indexer_settings()),
|
indexer_settings: Some(indexer_settings()),
|
||||||
indexer_test_all_results: Some(indexer_test_all_results),
|
indexer_test_all_results: Some(indexer_test_all_results),
|
||||||
indexer_test_errors: Some("error".to_string()),
|
indexer_test_errors: Some("error".to_string()),
|
||||||
|
start_time: DateTime::from(DateTime::parse_from_rfc3339("2023-05-20T21:29:16Z").unwrap()),
|
||||||
tags_map: tags_map(),
|
tags_map: tags_map(),
|
||||||
|
updates: updates(),
|
||||||
|
version: "1.2.3.4".to_owned(),
|
||||||
..LidarrData::default()
|
..LidarrData::default()
|
||||||
};
|
};
|
||||||
lidarr_data.albums.set_items(vec![album()]);
|
lidarr_data.albums.set_items(vec![album()]);
|
||||||
@@ -292,11 +316,14 @@ impl LidarrData<'_> {
|
|||||||
lidarr_data.history.filter = Some("test filter".into());
|
lidarr_data.history.filter = Some("test filter".into());
|
||||||
lidarr_data.root_folders.set_items(vec![root_folder()]);
|
lidarr_data.root_folders.set_items(vec![root_folder()]);
|
||||||
lidarr_data.indexers.set_items(vec![indexer()]);
|
lidarr_data.indexers.set_items(vec![indexer()]);
|
||||||
lidarr_data.version = "1.0.0".to_owned();
|
lidarr_data.queued_events.set_items(vec![queued_event()]);
|
||||||
lidarr_data.add_artist_search = Some("Test Artist".into());
|
lidarr_data.add_artist_search = Some("Test Artist".into());
|
||||||
let mut add_searched_artists = StatefulTable::default();
|
let mut add_searched_artists = StatefulTable::default();
|
||||||
add_searched_artists.set_items(vec![add_artist_search_result()]);
|
add_searched_artists.set_items(vec![add_artist_search_result()]);
|
||||||
lidarr_data.add_searched_artists = Some(add_searched_artists);
|
lidarr_data.add_searched_artists = Some(add_searched_artists);
|
||||||
|
lidarr_data.logs.set_items(vec![log_line().into()]);
|
||||||
|
lidarr_data.log_details.set_items(vec![log_line().into()]);
|
||||||
|
lidarr_data.tasks.set_items(vec![task()]);
|
||||||
|
|
||||||
lidarr_data
|
lidarr_data
|
||||||
}
|
}
|
||||||
@@ -385,6 +412,12 @@ pub enum ActiveLidarrBlock {
|
|||||||
SearchArtistsError,
|
SearchArtistsError,
|
||||||
SearchHistory,
|
SearchHistory,
|
||||||
SearchHistoryError,
|
SearchHistoryError,
|
||||||
|
System,
|
||||||
|
SystemLogs,
|
||||||
|
SystemQueuedEvents,
|
||||||
|
SystemTasks,
|
||||||
|
SystemTaskStartConfirmPrompt,
|
||||||
|
SystemUpdates,
|
||||||
UpdateAllArtistsPrompt,
|
UpdateAllArtistsPrompt,
|
||||||
UpdateAndScanArtistPrompt,
|
UpdateAndScanArtistPrompt,
|
||||||
UpdateDownloadsPrompt,
|
UpdateDownloadsPrompt,
|
||||||
@@ -611,6 +644,14 @@ pub static INDEXERS_BLOCKS: [ActiveLidarrBlock; 3] = [
|
|||||||
ActiveLidarrBlock::TestIndexer,
|
ActiveLidarrBlock::TestIndexer,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
pub static SYSTEM_DETAILS_BLOCKS: [ActiveLidarrBlock; 5] = [
|
||||||
|
ActiveLidarrBlock::SystemLogs,
|
||||||
|
ActiveLidarrBlock::SystemQueuedEvents,
|
||||||
|
ActiveLidarrBlock::SystemTasks,
|
||||||
|
ActiveLidarrBlock::SystemTaskStartConfirmPrompt,
|
||||||
|
ActiveLidarrBlock::SystemUpdates,
|
||||||
|
];
|
||||||
|
|
||||||
impl From<ActiveLidarrBlock> for Route {
|
impl From<ActiveLidarrBlock> for Route {
|
||||||
fn from(active_lidarr_block: ActiveLidarrBlock) -> Route {
|
fn from(active_lidarr_block: ActiveLidarrBlock) -> Route {
|
||||||
Route::Lidarr(active_lidarr_block, None)
|
Route::Lidarr(active_lidarr_block, None)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
mod tests {
|
mod tests {
|
||||||
use crate::app::context_clues::{
|
use crate::app::context_clues::{
|
||||||
DOWNLOADS_CONTEXT_CLUES, HISTORY_CONTEXT_CLUES, INDEXERS_CONTEXT_CLUES,
|
DOWNLOADS_CONTEXT_CLUES, HISTORY_CONTEXT_CLUES, INDEXERS_CONTEXT_CLUES,
|
||||||
ROOT_FOLDERS_CONTEXT_CLUES,
|
ROOT_FOLDERS_CONTEXT_CLUES, SYSTEM_CONTEXT_CLUES,
|
||||||
};
|
};
|
||||||
use crate::app::lidarr::lidarr_context_clues::{
|
use crate::app::lidarr::lidarr_context_clues::{
|
||||||
ARTIST_DETAILS_CONTEXT_CLUES, ARTISTS_CONTEXT_CLUES,
|
ARTIST_DETAILS_CONTEXT_CLUES, ARTISTS_CONTEXT_CLUES,
|
||||||
@@ -14,7 +14,7 @@ mod tests {
|
|||||||
DELETE_ARTIST_SELECTION_BLOCKS, DOWNLOADS_BLOCKS, EDIT_ARTIST_BLOCKS,
|
DELETE_ARTIST_SELECTION_BLOCKS, DOWNLOADS_BLOCKS, EDIT_ARTIST_BLOCKS,
|
||||||
EDIT_ARTIST_SELECTION_BLOCKS, EDIT_INDEXER_BLOCKS, EDIT_INDEXER_NZB_SELECTION_BLOCKS,
|
EDIT_ARTIST_SELECTION_BLOCKS, EDIT_INDEXER_BLOCKS, EDIT_INDEXER_NZB_SELECTION_BLOCKS,
|
||||||
EDIT_INDEXER_TORRENT_SELECTION_BLOCKS, HISTORY_BLOCKS, INDEXER_SETTINGS_BLOCKS,
|
EDIT_INDEXER_TORRENT_SELECTION_BLOCKS, HISTORY_BLOCKS, INDEXER_SETTINGS_BLOCKS,
|
||||||
INDEXER_SETTINGS_SELECTION_BLOCKS, INDEXERS_BLOCKS, ROOT_FOLDERS_BLOCKS,
|
INDEXER_SETTINGS_SELECTION_BLOCKS, INDEXERS_BLOCKS, ROOT_FOLDERS_BLOCKS, SYSTEM_DETAILS_BLOCKS,
|
||||||
};
|
};
|
||||||
use crate::models::{
|
use crate::models::{
|
||||||
BlockSelectionState, Route,
|
BlockSelectionState, Route,
|
||||||
@@ -143,17 +143,22 @@ mod tests {
|
|||||||
assert_none!(lidarr_data.edit_artist_modal);
|
assert_none!(lidarr_data.edit_artist_modal);
|
||||||
assert_none!(lidarr_data.add_root_folder_modal);
|
assert_none!(lidarr_data.add_root_folder_modal);
|
||||||
assert_is_empty!(lidarr_data.history);
|
assert_is_empty!(lidarr_data.history);
|
||||||
|
assert_is_empty!(lidarr_data.logs);
|
||||||
|
assert_is_empty!(lidarr_data.log_details);
|
||||||
assert_is_empty!(lidarr_data.metadata_profile_map);
|
assert_is_empty!(lidarr_data.metadata_profile_map);
|
||||||
assert!(!lidarr_data.prompt_confirm);
|
assert!(!lidarr_data.prompt_confirm);
|
||||||
assert_none!(lidarr_data.prompt_confirm_action);
|
assert_none!(lidarr_data.prompt_confirm_action);
|
||||||
assert_is_empty!(lidarr_data.quality_profile_map);
|
assert_is_empty!(lidarr_data.quality_profile_map);
|
||||||
|
assert_is_empty!(lidarr_data.queued_events);
|
||||||
assert_is_empty!(lidarr_data.root_folders);
|
assert_is_empty!(lidarr_data.root_folders);
|
||||||
assert_eq!(lidarr_data.selected_block, BlockSelectionState::default());
|
assert_eq!(lidarr_data.selected_block, BlockSelectionState::default());
|
||||||
assert_eq!(lidarr_data.start_time, <DateTime<Utc>>::default());
|
assert_eq!(lidarr_data.start_time, <DateTime<Utc>>::default());
|
||||||
assert_is_empty!(lidarr_data.tags_map);
|
assert_is_empty!(lidarr_data.tags_map);
|
||||||
|
assert_is_empty!(lidarr_data.tasks);
|
||||||
|
assert_is_empty!(lidarr_data.updates);
|
||||||
assert_is_empty!(lidarr_data.version);
|
assert_is_empty!(lidarr_data.version);
|
||||||
|
|
||||||
assert_eq!(lidarr_data.main_tabs.tabs.len(), 5);
|
assert_eq!(lidarr_data.main_tabs.tabs.len(), 6);
|
||||||
|
|
||||||
assert_str_eq!(lidarr_data.main_tabs.tabs[0].title, "Library");
|
assert_str_eq!(lidarr_data.main_tabs.tabs[0].title, "Library");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -210,6 +215,17 @@ mod tests {
|
|||||||
);
|
);
|
||||||
assert_none!(lidarr_data.main_tabs.tabs[4].config);
|
assert_none!(lidarr_data.main_tabs.tabs[4].config);
|
||||||
|
|
||||||
|
assert_str_eq!(lidarr_data.main_tabs.tabs[5].title, "System");
|
||||||
|
assert_eq!(
|
||||||
|
lidarr_data.main_tabs.tabs[5].route,
|
||||||
|
ActiveLidarrBlock::System.into()
|
||||||
|
);
|
||||||
|
assert_some_eq_x!(
|
||||||
|
&lidarr_data.main_tabs.tabs[5].contextual_help,
|
||||||
|
&SYSTEM_CONTEXT_CLUES
|
||||||
|
);
|
||||||
|
assert_none!(lidarr_data.main_tabs.tabs[5].config);
|
||||||
|
|
||||||
assert_eq!(lidarr_data.artist_info_tabs.tabs.len(), 1);
|
assert_eq!(lidarr_data.artist_info_tabs.tabs.len(), 1);
|
||||||
assert_str_eq!(lidarr_data.artist_info_tabs.tabs[0].title, "Albums");
|
assert_str_eq!(lidarr_data.artist_info_tabs.tabs[0].title, "Albums");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -605,4 +621,14 @@ mod tests {
|
|||||||
);
|
);
|
||||||
assert!(ADD_ROOT_FOLDER_BLOCKS.contains(&ActiveLidarrBlock::AddRootFolderTagsInput));
|
assert!(ADD_ROOT_FOLDER_BLOCKS.contains(&ActiveLidarrBlock::AddRootFolderTagsInput));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_system_details_blocks_contents() {
|
||||||
|
assert_eq!(SYSTEM_DETAILS_BLOCKS.len(), 5);
|
||||||
|
assert!(SYSTEM_DETAILS_BLOCKS.contains(&ActiveLidarrBlock::SystemLogs));
|
||||||
|
assert!(SYSTEM_DETAILS_BLOCKS.contains(&ActiveLidarrBlock::SystemQueuedEvents));
|
||||||
|
assert!(SYSTEM_DETAILS_BLOCKS.contains(&ActiveLidarrBlock::SystemTasks));
|
||||||
|
assert!(SYSTEM_DETAILS_BLOCKS.contains(&ActiveLidarrBlock::SystemTaskStartConfirmPrompt));
|
||||||
|
assert!(SYSTEM_DETAILS_BLOCKS.contains(&ActiveLidarrBlock::SystemUpdates));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,20 @@
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub mod test_utils {
|
pub mod test_utils {
|
||||||
use crate::models::HorizontallyScrollableText;
|
|
||||||
use crate::models::lidarr_models::{
|
use crate::models::lidarr_models::{
|
||||||
AddArtistSearchResult, Album, AlbumStatistics, Artist, ArtistStatistics, ArtistStatus,
|
AddArtistSearchResult, Album, AlbumStatistics, Artist, ArtistStatistics, ArtistStatus,
|
||||||
DownloadRecord, DownloadStatus, DownloadsResponse, EditArtistParams, LidarrHistoryData,
|
DownloadRecord, DownloadStatus, DownloadsResponse, EditArtistParams, LidarrHistoryData,
|
||||||
LidarrHistoryEventType, LidarrHistoryItem, LidarrHistoryWrapper, Member, MetadataProfile,
|
LidarrHistoryEventType, LidarrHistoryItem, LidarrHistoryWrapper, LidarrTask, LidarrTaskName,
|
||||||
NewItemMonitorType, Ratings, SystemStatus,
|
Member, MetadataProfile, NewItemMonitorType, Ratings, SystemStatus,
|
||||||
};
|
};
|
||||||
use crate::models::servarr_models::IndexerSettings;
|
use crate::models::servarr_models::IndexerSettings;
|
||||||
use crate::models::servarr_models::{
|
use crate::models::servarr_models::{
|
||||||
Indexer, IndexerField, Quality, QualityProfile, QualityWrapper, RootFolder, Tag,
|
Indexer, IndexerField, Quality, QualityProfile, QualityWrapper, RootFolder, Tag,
|
||||||
};
|
};
|
||||||
|
use crate::models::{HorizontallyScrollableText, ScrollableText};
|
||||||
use bimap::BiMap;
|
use bimap::BiMap;
|
||||||
use chrono::DateTime;
|
use chrono::DateTime;
|
||||||
|
use indoc::formatdoc;
|
||||||
use serde_json::{Number, json};
|
use serde_json::{Number, json};
|
||||||
|
|
||||||
pub const ADD_ARTIST_SEARCH_RESULT_JSON: &str = r#"{
|
pub const ADD_ARTIST_SEARCH_RESULT_JSON: &str = r#"{
|
||||||
@@ -333,4 +334,47 @@ pub mod test_utils {
|
|||||||
rss_sync_interval: 60,
|
rss_sync_interval: 60,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn task() -> LidarrTask {
|
||||||
|
LidarrTask {
|
||||||
|
name: "Backup".to_owned(),
|
||||||
|
task_name: LidarrTaskName::Backup,
|
||||||
|
interval: 60,
|
||||||
|
last_execution: DateTime::from(DateTime::parse_from_rfc3339("2023-05-20T21:29:16Z").unwrap()),
|
||||||
|
next_execution: DateTime::from(DateTime::parse_from_rfc3339("2023-05-20T22:29:16Z").unwrap()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn log_line() -> &'static str {
|
||||||
|
"2025-12-16 16:40:59 UTC|INFO|ImportListSyncService|No list items to process"
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn updates() -> ScrollableText {
|
||||||
|
let line_break = "-".repeat(200);
|
||||||
|
ScrollableText::with_string(formatdoc!(
|
||||||
|
"
|
||||||
|
The latest version of Lidarr is already installed
|
||||||
|
|
||||||
|
4.3.2.1 - 2023-04-15 02:02:53 UTC (Currently Installed)
|
||||||
|
{line_break}
|
||||||
|
New:
|
||||||
|
* Cool new thing
|
||||||
|
Fixed:
|
||||||
|
* Some bugs killed
|
||||||
|
|
||||||
|
|
||||||
|
3.2.1.0 - 2023-04-15 02:02:53 UTC (Previously Installed)
|
||||||
|
{line_break}
|
||||||
|
New:
|
||||||
|
* Cool new thing (old)
|
||||||
|
* Other cool new thing (old)
|
||||||
|
|
||||||
|
|
||||||
|
2.1.0 - 2023-04-15 02:02:53 UTC
|
||||||
|
{line_break}
|
||||||
|
Fixed:
|
||||||
|
* Killed bug 1
|
||||||
|
* Fixed bug 2"
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,7 +90,9 @@ mod tests {
|
|||||||
LidarrEvent::UpdateAllArtists,
|
LidarrEvent::UpdateAllArtists,
|
||||||
LidarrEvent::TriggerAutomaticArtistSearch(0),
|
LidarrEvent::TriggerAutomaticArtistSearch(0),
|
||||||
LidarrEvent::UpdateAndScanArtist(0),
|
LidarrEvent::UpdateAndScanArtist(0),
|
||||||
LidarrEvent::UpdateDownloads
|
LidarrEvent::UpdateDownloads,
|
||||||
|
LidarrEvent::GetQueuedEvents,
|
||||||
|
LidarrEvent::StartTask(Default::default())
|
||||||
)]
|
)]
|
||||||
event: LidarrEvent,
|
event: LidarrEvent,
|
||||||
) {
|
) {
|
||||||
@@ -128,6 +130,9 @@ mod tests {
|
|||||||
#[case(LidarrEvent::GetQualityProfiles, "/qualityprofile")]
|
#[case(LidarrEvent::GetQualityProfiles, "/qualityprofile")]
|
||||||
#[case(LidarrEvent::GetStatus, "/system/status")]
|
#[case(LidarrEvent::GetStatus, "/system/status")]
|
||||||
#[case(LidarrEvent::GetTags, "/tag")]
|
#[case(LidarrEvent::GetTags, "/tag")]
|
||||||
|
#[case(LidarrEvent::GetLogs(500), "/log")]
|
||||||
|
#[case(LidarrEvent::GetTasks, "/system/task")]
|
||||||
|
#[case(LidarrEvent::GetUpdates, "/update")]
|
||||||
#[case(LidarrEvent::HealthCheck, "/health")]
|
#[case(LidarrEvent::HealthCheck, "/health")]
|
||||||
#[case(LidarrEvent::MarkHistoryItemAsFailed(0), "/history/failed")]
|
#[case(LidarrEvent::MarkHistoryItemAsFailed(0), "/history/failed")]
|
||||||
#[case(LidarrEvent::TestIndexer(0), "/indexer/test")]
|
#[case(LidarrEvent::TestIndexer(0), "/indexer/test")]
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use log::info;
|
|||||||
use super::{NetworkEvent, NetworkResource};
|
use super::{NetworkEvent, NetworkResource};
|
||||||
use crate::models::lidarr_models::{
|
use crate::models::lidarr_models::{
|
||||||
AddArtistBody, AddLidarrRootFolderBody, DeleteParams, EditArtistParams, LidarrSerdeable,
|
AddArtistBody, AddLidarrRootFolderBody, DeleteParams, EditArtistParams, LidarrSerdeable,
|
||||||
MetadataProfile,
|
LidarrTaskName, MetadataProfile,
|
||||||
};
|
};
|
||||||
use crate::models::servarr_models::{EditIndexerParams, IndexerSettings, QualityProfile, Tag};
|
use crate::models::servarr_models::{EditIndexerParams, IndexerSettings, QualityProfile, Tag};
|
||||||
use crate::network::{Network, RequestMethod};
|
use crate::network::{Network, RequestMethod};
|
||||||
@@ -47,16 +47,21 @@ pub enum LidarrEvent {
|
|||||||
GetHistory(u64),
|
GetHistory(u64),
|
||||||
GetHostConfig,
|
GetHostConfig,
|
||||||
GetIndexers,
|
GetIndexers,
|
||||||
|
GetLogs(u64),
|
||||||
MarkHistoryItemAsFailed(i64),
|
MarkHistoryItemAsFailed(i64),
|
||||||
GetMetadataProfiles,
|
GetMetadataProfiles,
|
||||||
GetQualityProfiles,
|
GetQualityProfiles,
|
||||||
|
GetQueuedEvents,
|
||||||
GetRootFolders,
|
GetRootFolders,
|
||||||
GetSecurityConfig,
|
GetSecurityConfig,
|
||||||
GetStatus,
|
GetStatus,
|
||||||
|
GetUpdates,
|
||||||
GetTags,
|
GetTags,
|
||||||
|
GetTasks,
|
||||||
HealthCheck,
|
HealthCheck,
|
||||||
ListArtists,
|
ListArtists,
|
||||||
SearchNewArtist(String),
|
SearchNewArtist(String),
|
||||||
|
StartTask(LidarrTaskName),
|
||||||
TestIndexer(i64),
|
TestIndexer(i64),
|
||||||
TestAllIndexers,
|
TestAllIndexers,
|
||||||
ToggleAlbumMonitoring(i64),
|
ToggleAlbumMonitoring(i64),
|
||||||
@@ -84,6 +89,7 @@ impl NetworkResource for LidarrEvent {
|
|||||||
| LidarrEvent::ToggleAlbumMonitoring(_)
|
| LidarrEvent::ToggleAlbumMonitoring(_)
|
||||||
| LidarrEvent::GetAlbumDetails(_)
|
| LidarrEvent::GetAlbumDetails(_)
|
||||||
| LidarrEvent::DeleteAlbum(_) => "/album",
|
| LidarrEvent::DeleteAlbum(_) => "/album",
|
||||||
|
LidarrEvent::GetLogs(_) => "/log",
|
||||||
LidarrEvent::GetDiskSpace => "/diskspace",
|
LidarrEvent::GetDiskSpace => "/diskspace",
|
||||||
LidarrEvent::GetDownloads(_) | LidarrEvent::DeleteDownload(_) => "/queue",
|
LidarrEvent::GetDownloads(_) | LidarrEvent::DeleteDownload(_) => "/queue",
|
||||||
LidarrEvent::GetHistory(_) => "/history",
|
LidarrEvent::GetHistory(_) => "/history",
|
||||||
@@ -95,7 +101,9 @@ impl NetworkResource for LidarrEvent {
|
|||||||
LidarrEvent::TriggerAutomaticArtistSearch(_)
|
LidarrEvent::TriggerAutomaticArtistSearch(_)
|
||||||
| LidarrEvent::UpdateAllArtists
|
| LidarrEvent::UpdateAllArtists
|
||||||
| LidarrEvent::UpdateAndScanArtist(_)
|
| LidarrEvent::UpdateAndScanArtist(_)
|
||||||
| LidarrEvent::UpdateDownloads => "/command",
|
| LidarrEvent::UpdateDownloads
|
||||||
|
| LidarrEvent::GetQueuedEvents
|
||||||
|
| LidarrEvent::StartTask(_) => "/command",
|
||||||
LidarrEvent::GetMetadataProfiles => "/metadataprofile",
|
LidarrEvent::GetMetadataProfiles => "/metadataprofile",
|
||||||
LidarrEvent::GetQualityProfiles => "/qualityprofile",
|
LidarrEvent::GetQualityProfiles => "/qualityprofile",
|
||||||
LidarrEvent::GetRootFolders
|
LidarrEvent::GetRootFolders
|
||||||
@@ -104,6 +112,8 @@ impl NetworkResource for LidarrEvent {
|
|||||||
LidarrEvent::TestIndexer(_) => "/indexer/test",
|
LidarrEvent::TestIndexer(_) => "/indexer/test",
|
||||||
LidarrEvent::TestAllIndexers => "/indexer/testall",
|
LidarrEvent::TestAllIndexers => "/indexer/testall",
|
||||||
LidarrEvent::GetStatus => "/system/status",
|
LidarrEvent::GetStatus => "/system/status",
|
||||||
|
LidarrEvent::GetTasks => "/system/task",
|
||||||
|
LidarrEvent::GetUpdates => "/update",
|
||||||
LidarrEvent::HealthCheck => "/health",
|
LidarrEvent::HealthCheck => "/health",
|
||||||
LidarrEvent::SearchNewArtist(_) => "/artist/lookup",
|
LidarrEvent::SearchNewArtist(_) => "/artist/lookup",
|
||||||
}
|
}
|
||||||
@@ -182,6 +192,10 @@ impl Network<'_, '_> {
|
|||||||
.get_lidarr_history(events)
|
.get_lidarr_history(events)
|
||||||
.await
|
.await
|
||||||
.map(LidarrSerdeable::from),
|
.map(LidarrSerdeable::from),
|
||||||
|
LidarrEvent::GetLogs(events) => self
|
||||||
|
.get_lidarr_logs(events)
|
||||||
|
.await
|
||||||
|
.map(LidarrSerdeable::from),
|
||||||
LidarrEvent::MarkHistoryItemAsFailed(history_item_id) => self
|
LidarrEvent::MarkHistoryItemAsFailed(history_item_id) => self
|
||||||
.mark_lidarr_history_item_as_failed(history_item_id)
|
.mark_lidarr_history_item_as_failed(history_item_id)
|
||||||
.await
|
.await
|
||||||
@@ -198,6 +212,10 @@ impl Network<'_, '_> {
|
|||||||
.get_lidarr_quality_profiles()
|
.get_lidarr_quality_profiles()
|
||||||
.await
|
.await
|
||||||
.map(LidarrSerdeable::from),
|
.map(LidarrSerdeable::from),
|
||||||
|
LidarrEvent::GetQueuedEvents => self
|
||||||
|
.get_queued_lidarr_events()
|
||||||
|
.await
|
||||||
|
.map(LidarrSerdeable::from),
|
||||||
LidarrEvent::GetRootFolders => self
|
LidarrEvent::GetRootFolders => self
|
||||||
.get_lidarr_root_folders()
|
.get_lidarr_root_folders()
|
||||||
.await
|
.await
|
||||||
@@ -208,6 +226,8 @@ impl Network<'_, '_> {
|
|||||||
.map(LidarrSerdeable::from),
|
.map(LidarrSerdeable::from),
|
||||||
LidarrEvent::GetStatus => self.get_lidarr_status().await.map(LidarrSerdeable::from),
|
LidarrEvent::GetStatus => self.get_lidarr_status().await.map(LidarrSerdeable::from),
|
||||||
LidarrEvent::GetTags => self.get_lidarr_tags().await.map(LidarrSerdeable::from),
|
LidarrEvent::GetTags => self.get_lidarr_tags().await.map(LidarrSerdeable::from),
|
||||||
|
LidarrEvent::GetTasks => self.get_lidarr_tasks().await.map(LidarrSerdeable::from),
|
||||||
|
LidarrEvent::GetUpdates => self.get_lidarr_updates().await.map(LidarrSerdeable::from),
|
||||||
LidarrEvent::HealthCheck => self
|
LidarrEvent::HealthCheck => self
|
||||||
.get_lidarr_healthcheck()
|
.get_lidarr_healthcheck()
|
||||||
.await
|
.await
|
||||||
@@ -216,6 +236,10 @@ impl Network<'_, '_> {
|
|||||||
LidarrEvent::SearchNewArtist(query) => {
|
LidarrEvent::SearchNewArtist(query) => {
|
||||||
self.search_artist(query).await.map(LidarrSerdeable::from)
|
self.search_artist(query).await.map(LidarrSerdeable::from)
|
||||||
}
|
}
|
||||||
|
LidarrEvent::StartTask(task_name) => self
|
||||||
|
.start_lidarr_task(task_name)
|
||||||
|
.await
|
||||||
|
.map(LidarrSerdeable::from),
|
||||||
LidarrEvent::ToggleAlbumMonitoring(album_id) => self
|
LidarrEvent::ToggleAlbumMonitoring(album_id) => self
|
||||||
.toggle_album_monitoring(album_id)
|
.toggle_album_monitoring(album_id)
|
||||||
.await
|
.await
|
||||||
|
|||||||
@@ -1,9 +1,14 @@
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::models::lidarr_models::{LidarrSerdeable, SystemStatus};
|
use crate::models::HorizontallyScrollableText;
|
||||||
use crate::models::servarr_models::{DiskSpace, HostConfig, SecurityConfig};
|
use crate::models::lidarr_models::{LidarrSerdeable, LidarrTask, LidarrTaskName, SystemStatus};
|
||||||
|
use crate::models::servarr_models::{
|
||||||
|
DiskSpace, HostConfig, LogResponse, QueueEvent, SecurityConfig, Update,
|
||||||
|
};
|
||||||
use crate::network::lidarr_network::LidarrEvent;
|
use crate::network::lidarr_network::LidarrEvent;
|
||||||
|
use crate::network::lidarr_network::lidarr_network_test_utils::test_utils::updates;
|
||||||
use crate::network::network_tests::test_utils::{MockServarrApi, test_network};
|
use crate::network::network_tests::test_utils::{MockServarrApi, test_network};
|
||||||
|
use chrono::DateTime;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
||||||
@@ -104,6 +109,117 @@ mod tests {
|
|||||||
assert_eq!(security_config, response);
|
assert_eq!(security_config, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_handle_get_lidarr_logs_event() {
|
||||||
|
let expected_logs = vec![
|
||||||
|
HorizontallyScrollableText::from(
|
||||||
|
"2023-05-20 21:29:16 UTC|FATAL|LidarrError|Some.Big.Bad.Exception|test exception",
|
||||||
|
),
|
||||||
|
HorizontallyScrollableText::from("2023-05-20 21:29:16 UTC|INFO|TestLogger|test message"),
|
||||||
|
];
|
||||||
|
let logs_response_json = json!({
|
||||||
|
"page": 1,
|
||||||
|
"pageSize": 500,
|
||||||
|
"sortKey": "time",
|
||||||
|
"sortDirection": "descending",
|
||||||
|
"totalRecords": 2,
|
||||||
|
"records": [
|
||||||
|
{
|
||||||
|
"time": "2023-05-20T21:29:16Z",
|
||||||
|
"level": "info",
|
||||||
|
"logger": "TestLogger",
|
||||||
|
"message": "test message",
|
||||||
|
"id": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": "2023-05-20T21:29:16Z",
|
||||||
|
"level": "fatal",
|
||||||
|
"logger": "LidarrError",
|
||||||
|
"exception": "test exception",
|
||||||
|
"exceptionType": "Some.Big.Bad.Exception",
|
||||||
|
"id": 2
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
let response: LogResponse = serde_json::from_value(logs_response_json.clone()).unwrap();
|
||||||
|
let (mock, app, _server) = MockServarrApi::get()
|
||||||
|
.returns(logs_response_json)
|
||||||
|
.query("pageSize=500&sortDirection=descending&sortKey=time")
|
||||||
|
.build_for(LidarrEvent::GetLogs(500))
|
||||||
|
.await;
|
||||||
|
app.lock().await.server_tabs.set_index(2);
|
||||||
|
let mut network = test_network(&app);
|
||||||
|
|
||||||
|
let LidarrSerdeable::LogResponse(logs) = network
|
||||||
|
.handle_lidarr_event(LidarrEvent::GetLogs(500))
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
else {
|
||||||
|
panic!("Expected LogResponse")
|
||||||
|
};
|
||||||
|
mock.assert_async().await;
|
||||||
|
assert_eq!(app.lock().await.data.lidarr_data.logs.items, expected_logs);
|
||||||
|
assert!(
|
||||||
|
app
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.logs
|
||||||
|
.current_selection()
|
||||||
|
.text
|
||||||
|
.contains("INFO")
|
||||||
|
);
|
||||||
|
assert_eq!(logs, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_handle_get_queued_lidarr_events_event() {
|
||||||
|
let queued_events_json = json!([{
|
||||||
|
"name": "RefreshMonitoredDownloads",
|
||||||
|
"commandName": "Refresh Monitored Downloads",
|
||||||
|
"status": "completed",
|
||||||
|
"queued": "2023-05-20T21:29:16Z",
|
||||||
|
"started": "2023-05-20T21:29:16Z",
|
||||||
|
"ended": "2023-05-20T21:29:16Z",
|
||||||
|
"duration": "00:00:00.5111547",
|
||||||
|
"trigger": "scheduled",
|
||||||
|
}]);
|
||||||
|
let response: Vec<QueueEvent> = serde_json::from_value(queued_events_json.clone()).unwrap();
|
||||||
|
let timestamp = DateTime::from(DateTime::parse_from_rfc3339("2023-05-20T21:29:16Z").unwrap());
|
||||||
|
let expected_event = QueueEvent {
|
||||||
|
name: "RefreshMonitoredDownloads".to_owned(),
|
||||||
|
command_name: "Refresh Monitored Downloads".to_owned(),
|
||||||
|
status: "completed".to_owned(),
|
||||||
|
queued: timestamp,
|
||||||
|
started: Some(timestamp),
|
||||||
|
ended: Some(timestamp),
|
||||||
|
duration: Some("00:00:00.5111547".to_owned()),
|
||||||
|
trigger: "scheduled".to_owned(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let (mock, app, _server) = MockServarrApi::get()
|
||||||
|
.returns(queued_events_json)
|
||||||
|
.build_for(LidarrEvent::GetQueuedEvents)
|
||||||
|
.await;
|
||||||
|
app.lock().await.server_tabs.set_index(2);
|
||||||
|
let mut network = test_network(&app);
|
||||||
|
|
||||||
|
let LidarrSerdeable::QueueEvents(events) = network
|
||||||
|
.handle_lidarr_event(LidarrEvent::GetQueuedEvents)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
else {
|
||||||
|
panic!("Expected QueueEvents")
|
||||||
|
};
|
||||||
|
mock.assert_async().await;
|
||||||
|
assert_eq!(
|
||||||
|
app.lock().await.data.lidarr_data.queued_events.items,
|
||||||
|
vec![expected_event]
|
||||||
|
);
|
||||||
|
assert_eq!(events, response);
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_handle_get_status_event() {
|
async fn test_handle_get_status_event() {
|
||||||
let status_json = json!({
|
let status_json = json!({
|
||||||
@@ -129,4 +245,171 @@ mod tests {
|
|||||||
assert_eq!(status, response);
|
assert_eq!(status, response);
|
||||||
assert_eq!(app.lock().await.data.lidarr_data.version, "1.0.0");
|
assert_eq!(app.lock().await.data.lidarr_data.version, "1.0.0");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_handle_get_lidarr_tasks_event() {
|
||||||
|
let tasks_json = json!([{
|
||||||
|
"name": "Application Update Check",
|
||||||
|
"taskName": "ApplicationUpdateCheck",
|
||||||
|
"interval": 360,
|
||||||
|
"lastExecution": "2023-05-20T21:29:16Z",
|
||||||
|
"nextExecution": "2023-05-20T21:29:16Z",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Backup",
|
||||||
|
"taskName": "Backup",
|
||||||
|
"interval": 10080,
|
||||||
|
"lastExecution": "2023-05-20T21:29:16Z",
|
||||||
|
"nextExecution": "2023-05-20T21:29:16Z",
|
||||||
|
}]);
|
||||||
|
let response: Vec<LidarrTask> = serde_json::from_value(tasks_json.clone()).unwrap();
|
||||||
|
let timestamp = DateTime::from(DateTime::parse_from_rfc3339("2023-05-20T21:29:16Z").unwrap());
|
||||||
|
let expected_tasks = vec![
|
||||||
|
LidarrTask {
|
||||||
|
name: "Application Update Check".to_owned(),
|
||||||
|
task_name: LidarrTaskName::ApplicationUpdateCheck,
|
||||||
|
interval: 360,
|
||||||
|
last_execution: timestamp,
|
||||||
|
next_execution: timestamp,
|
||||||
|
},
|
||||||
|
LidarrTask {
|
||||||
|
name: "Backup".to_owned(),
|
||||||
|
task_name: LidarrTaskName::Backup,
|
||||||
|
interval: 10080,
|
||||||
|
last_execution: timestamp,
|
||||||
|
next_execution: timestamp,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
let (mock, app, _server) = MockServarrApi::get()
|
||||||
|
.returns(tasks_json)
|
||||||
|
.build_for(LidarrEvent::GetTasks)
|
||||||
|
.await;
|
||||||
|
app.lock().await.server_tabs.set_index(2);
|
||||||
|
let mut network = test_network(&app);
|
||||||
|
|
||||||
|
let LidarrSerdeable::Tasks(tasks) = network
|
||||||
|
.handle_lidarr_event(LidarrEvent::GetTasks)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
else {
|
||||||
|
panic!("Expected Tasks")
|
||||||
|
};
|
||||||
|
mock.assert_async().await;
|
||||||
|
assert_eq!(
|
||||||
|
app.lock().await.data.lidarr_data.tasks.items,
|
||||||
|
expected_tasks
|
||||||
|
);
|
||||||
|
assert_eq!(tasks, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_handle_get_lidarr_updates_event() {
|
||||||
|
let updates_json = json!([{
|
||||||
|
"version": "4.3.2.1",
|
||||||
|
"releaseDate": "2023-04-15T02:02:53Z",
|
||||||
|
"installed": true,
|
||||||
|
"installedOn": "2023-04-15T02:02:53Z",
|
||||||
|
"latest": true,
|
||||||
|
"changes": {
|
||||||
|
"new": [
|
||||||
|
"Cool new thing"
|
||||||
|
],
|
||||||
|
"fixed": [
|
||||||
|
"Some bugs killed"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "3.2.1.0",
|
||||||
|
"releaseDate": "2023-04-15T02:02:53Z",
|
||||||
|
"installed": false,
|
||||||
|
"installedOn": "2023-04-15T02:02:53Z",
|
||||||
|
"latest": false,
|
||||||
|
"changes": {
|
||||||
|
"new": [
|
||||||
|
"Cool new thing (old)",
|
||||||
|
"Other cool new thing (old)"
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "2.1.0",
|
||||||
|
"releaseDate": "2023-04-15T02:02:53Z",
|
||||||
|
"installed": false,
|
||||||
|
"latest": false,
|
||||||
|
"changes": {
|
||||||
|
"fixed": [
|
||||||
|
"Killed bug 1",
|
||||||
|
"Fixed bug 2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
}]);
|
||||||
|
let response: Vec<Update> = serde_json::from_value(updates_json.clone()).unwrap();
|
||||||
|
let expected_text = updates();
|
||||||
|
let (mock, app, _server) = MockServarrApi::get()
|
||||||
|
.returns(updates_json)
|
||||||
|
.build_for(LidarrEvent::GetUpdates)
|
||||||
|
.await;
|
||||||
|
app.lock().await.server_tabs.set_index(2);
|
||||||
|
let mut network = test_network(&app);
|
||||||
|
|
||||||
|
let LidarrSerdeable::Updates(updates) = network
|
||||||
|
.handle_lidarr_event(LidarrEvent::GetUpdates)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
else {
|
||||||
|
panic!("Expected Updates")
|
||||||
|
};
|
||||||
|
mock.assert_async().await;
|
||||||
|
let actual_text = app.lock().await.data.lidarr_data.updates.get_text();
|
||||||
|
let expected = expected_text.get_text();
|
||||||
|
|
||||||
|
// Trim trailing whitespace from each line for comparison
|
||||||
|
let actual_trimmed: Vec<&str> = actual_text.lines().map(|l| l.trim_end()).collect();
|
||||||
|
let expected_trimmed: Vec<&str> = expected.lines().map(|l| l.trim_end()).collect();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
actual_trimmed, expected_trimmed,
|
||||||
|
"Updates text mismatch (after trimming trailing whitespace)"
|
||||||
|
);
|
||||||
|
assert_eq!(updates, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_handle_start_lidarr_task_event() {
|
||||||
|
let response = json!({ "test": "test"});
|
||||||
|
let (mock, app, _server) = MockServarrApi::post()
|
||||||
|
.with_request_body(json!({
|
||||||
|
"name": "ApplicationUpdateCheck"
|
||||||
|
}))
|
||||||
|
.returns(response.clone())
|
||||||
|
.build_for(LidarrEvent::StartTask(
|
||||||
|
LidarrTaskName::ApplicationUpdateCheck,
|
||||||
|
))
|
||||||
|
.await;
|
||||||
|
app
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.tasks
|
||||||
|
.set_items(vec![LidarrTask {
|
||||||
|
task_name: LidarrTaskName::default(),
|
||||||
|
..LidarrTask::default()
|
||||||
|
}]);
|
||||||
|
app.lock().await.server_tabs.set_index(2);
|
||||||
|
let mut network = test_network(&app);
|
||||||
|
|
||||||
|
let LidarrSerdeable::Value(value) = network
|
||||||
|
.handle_lidarr_event(LidarrEvent::StartTask(
|
||||||
|
LidarrTaskName::ApplicationUpdateCheck,
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
else {
|
||||||
|
panic!("Expected Value")
|
||||||
|
};
|
||||||
|
mock.assert_async().await;
|
||||||
|
assert_eq!(value, response);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
use anyhow::Result;
|
use crate::models::lidarr_models::{LidarrTask, LidarrTaskName, SystemStatus};
|
||||||
use log::info;
|
use crate::models::servarr_models::{
|
||||||
|
CommandBody, DiskSpace, HostConfig, LogResponse, QueueEvent, SecurityConfig, Update,
|
||||||
use crate::models::lidarr_models::SystemStatus;
|
};
|
||||||
use crate::models::servarr_models::{DiskSpace, HostConfig, SecurityConfig};
|
use crate::models::{HorizontallyScrollableText, Scrollable, ScrollableText};
|
||||||
use crate::network::lidarr_network::LidarrEvent;
|
use crate::network::lidarr_network::LidarrEvent;
|
||||||
use crate::network::{Network, RequestMethod};
|
use crate::network::{Network, RequestMethod};
|
||||||
|
use anyhow::Result;
|
||||||
|
use indoc::formatdoc;
|
||||||
|
use log::info;
|
||||||
|
use serde_json::Value;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[path = "lidarr_system_network_tests.rs"]
|
#[path = "lidarr_system_network_tests.rs"]
|
||||||
@@ -26,18 +30,62 @@ impl Network<'_, '_> {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(in crate::network::lidarr_network) async fn get_lidarr_security_config(
|
pub(in crate::network::lidarr_network) async fn get_lidarr_logs(
|
||||||
&mut self,
|
&mut self,
|
||||||
) -> Result<SecurityConfig> {
|
events: u64,
|
||||||
info!("Fetching Lidarr security config");
|
) -> Result<LogResponse> {
|
||||||
let event = LidarrEvent::GetSecurityConfig;
|
info!("Fetching Lidarr logs");
|
||||||
|
let event = LidarrEvent::GetLogs(events);
|
||||||
|
|
||||||
|
let params = format!("pageSize={events}&sortDirection=descending&sortKey=time");
|
||||||
let request_props = self
|
let request_props = self
|
||||||
.request_props_from(event, RequestMethod::Get, None::<()>, None, None)
|
.request_props_from(event, RequestMethod::Get, None::<()>, None, Some(params))
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
self
|
self
|
||||||
.handle_request::<(), SecurityConfig>(request_props, |_, _| ())
|
.handle_request::<(), LogResponse>(request_props, |log_response, mut app| {
|
||||||
|
let mut logs = log_response.records;
|
||||||
|
logs.reverse();
|
||||||
|
|
||||||
|
let log_lines = logs
|
||||||
|
.into_iter()
|
||||||
|
.map(|log| {
|
||||||
|
if log.exception.is_some() {
|
||||||
|
HorizontallyScrollableText::from(format!(
|
||||||
|
"{}|{}|{}|{}|{}",
|
||||||
|
log.time,
|
||||||
|
log.level.to_uppercase(),
|
||||||
|
log
|
||||||
|
.logger
|
||||||
|
.as_ref()
|
||||||
|
.expect("logger must exist when exception is present"),
|
||||||
|
log
|
||||||
|
.exception_type
|
||||||
|
.as_ref()
|
||||||
|
.expect("exception_type must exist when exception is present"),
|
||||||
|
log
|
||||||
|
.exception
|
||||||
|
.as_ref()
|
||||||
|
.expect("exception must exist in this branch")
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
HorizontallyScrollableText::from(format!(
|
||||||
|
"{}|{}|{}|{}",
|
||||||
|
log.time,
|
||||||
|
log.level.to_uppercase(),
|
||||||
|
log.logger.as_ref().expect("logger must exist in log entry"),
|
||||||
|
log
|
||||||
|
.message
|
||||||
|
.as_ref()
|
||||||
|
.expect("message must exist when exception is not present")
|
||||||
|
))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
app.data.lidarr_data.logs.set_items(log_lines);
|
||||||
|
app.data.lidarr_data.logs.scroll_to_bottom();
|
||||||
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,6 +106,42 @@ impl Network<'_, '_> {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(in crate::network::lidarr_network) async fn get_queued_lidarr_events(
|
||||||
|
&mut self,
|
||||||
|
) -> Result<Vec<QueueEvent>> {
|
||||||
|
info!("Fetching Lidarr queued events");
|
||||||
|
let event = LidarrEvent::GetQueuedEvents;
|
||||||
|
|
||||||
|
let request_props = self
|
||||||
|
.request_props_from(event, RequestMethod::Get, None::<()>, None, None)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
self
|
||||||
|
.handle_request::<(), Vec<QueueEvent>>(request_props, |queued_events_vec, mut app| {
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.queued_events
|
||||||
|
.set_items(queued_events_vec);
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(in crate::network::lidarr_network) async fn get_lidarr_security_config(
|
||||||
|
&mut self,
|
||||||
|
) -> Result<SecurityConfig> {
|
||||||
|
info!("Fetching Lidarr security config");
|
||||||
|
let event = LidarrEvent::GetSecurityConfig;
|
||||||
|
|
||||||
|
let request_props = self
|
||||||
|
.request_props_from(event, RequestMethod::Get, None::<()>, None, None)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
self
|
||||||
|
.handle_request::<(), SecurityConfig>(request_props, |_, _| ())
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
pub(in crate::network::lidarr_network) async fn get_lidarr_status(
|
pub(in crate::network::lidarr_network) async fn get_lidarr_status(
|
||||||
&mut self,
|
&mut self,
|
||||||
) -> Result<SystemStatus> {
|
) -> Result<SystemStatus> {
|
||||||
@@ -75,4 +159,120 @@ impl Network<'_, '_> {
|
|||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(in crate::network::lidarr_network) async fn get_lidarr_tasks(
|
||||||
|
&mut self,
|
||||||
|
) -> Result<Vec<LidarrTask>> {
|
||||||
|
info!("Fetching Lidarr tasks");
|
||||||
|
let event = LidarrEvent::GetTasks;
|
||||||
|
|
||||||
|
let request_props = self
|
||||||
|
.request_props_from(event, RequestMethod::Get, None::<()>, None, None)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
self
|
||||||
|
.handle_request::<(), Vec<LidarrTask>>(request_props, |tasks_vec, mut app| {
|
||||||
|
app.data.lidarr_data.tasks.set_items(tasks_vec);
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(in crate::network::lidarr_network) async fn get_lidarr_updates(
|
||||||
|
&mut self,
|
||||||
|
) -> Result<Vec<Update>> {
|
||||||
|
info!("Fetching Lidarr updates");
|
||||||
|
let event = LidarrEvent::GetUpdates;
|
||||||
|
|
||||||
|
let request_props = self
|
||||||
|
.request_props_from(event, RequestMethod::Get, None::<()>, None, None)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
self
|
||||||
|
.handle_request::<(), Vec<Update>>(request_props, |updates_vec, mut app| {
|
||||||
|
let latest_installed = if updates_vec
|
||||||
|
.iter()
|
||||||
|
.any(|update| update.latest && update.installed_on.is_some())
|
||||||
|
{
|
||||||
|
"already".to_owned()
|
||||||
|
} else {
|
||||||
|
"not".to_owned()
|
||||||
|
};
|
||||||
|
let updates = updates_vec
|
||||||
|
.into_iter()
|
||||||
|
.map(|update| {
|
||||||
|
let install_status = if update.installed_on.is_some() {
|
||||||
|
if update.installed {
|
||||||
|
" (Currently Installed)".to_owned()
|
||||||
|
} else {
|
||||||
|
" (Previously Installed)".to_owned()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
|
};
|
||||||
|
let vec_to_bullet_points = |vec: Vec<String>| {
|
||||||
|
vec
|
||||||
|
.iter()
|
||||||
|
.map(|change| format!(" * {change}"))
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join("\n")
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut update_info = formatdoc!(
|
||||||
|
"{} - {}{install_status}
|
||||||
|
{}",
|
||||||
|
update.version,
|
||||||
|
update.release_date,
|
||||||
|
"-".repeat(200)
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some(new_changes) = update.changes.new {
|
||||||
|
let changes = vec_to_bullet_points(new_changes);
|
||||||
|
update_info = formatdoc!(
|
||||||
|
"{update_info}
|
||||||
|
New:
|
||||||
|
{changes}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(fixes) = update.changes.fixed {
|
||||||
|
let fixes = vec_to_bullet_points(fixes);
|
||||||
|
update_info = formatdoc!(
|
||||||
|
"{update_info}
|
||||||
|
Fixed:
|
||||||
|
{fixes}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
update_info
|
||||||
|
})
|
||||||
|
.reduce(|version_1, version_2| format!("{version_1}\n\n\n{version_2}"))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
app.data.lidarr_data.updates = ScrollableText::with_string(formatdoc!(
|
||||||
|
"The latest version of Lidarr is {latest_installed} installed
|
||||||
|
|
||||||
|
{updates}"
|
||||||
|
));
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(in crate::network::lidarr_network) async fn start_lidarr_task(
|
||||||
|
&mut self,
|
||||||
|
task: LidarrTaskName,
|
||||||
|
) -> Result<Value> {
|
||||||
|
let event = LidarrEvent::StartTask(task);
|
||||||
|
let task_name = task.to_string();
|
||||||
|
info!("Starting Lidarr task: {task_name}");
|
||||||
|
|
||||||
|
let body = CommandBody { name: task_name };
|
||||||
|
|
||||||
|
let request_props = self
|
||||||
|
.request_props_from(event, RequestMethod::Post, Some(body), None, None)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
self
|
||||||
|
.handle_request::<CommandBody, Value>(request_props, |_, _| ())
|
||||||
|
.await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ use super::{
|
|||||||
use crate::ui::lidarr_ui::downloads::DownloadsUi;
|
use crate::ui::lidarr_ui::downloads::DownloadsUi;
|
||||||
use crate::ui::lidarr_ui::indexers::IndexersUi;
|
use crate::ui::lidarr_ui::indexers::IndexersUi;
|
||||||
use crate::ui::lidarr_ui::root_folders::RootFoldersUi;
|
use crate::ui::lidarr_ui::root_folders::RootFoldersUi;
|
||||||
|
use crate::ui::lidarr_ui::system::SystemUi;
|
||||||
use crate::{
|
use crate::{
|
||||||
app::App,
|
app::App,
|
||||||
logos::LIDARR_LOGO,
|
logos::LIDARR_LOGO,
|
||||||
@@ -43,11 +44,12 @@ mod history;
|
|||||||
mod indexers;
|
mod indexers;
|
||||||
mod library;
|
mod library;
|
||||||
mod lidarr_ui_utils;
|
mod lidarr_ui_utils;
|
||||||
|
mod root_folders;
|
||||||
|
mod system;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[path = "lidarr_ui_tests.rs"]
|
#[path = "lidarr_ui_tests.rs"]
|
||||||
mod lidarr_ui_tests;
|
mod lidarr_ui_tests;
|
||||||
mod root_folders;
|
|
||||||
|
|
||||||
pub(super) struct LidarrUi;
|
pub(super) struct LidarrUi;
|
||||||
|
|
||||||
@@ -66,6 +68,7 @@ impl DrawUi for LidarrUi {
|
|||||||
_ if HistoryUi::accepts(route) => HistoryUi::draw(f, app, content_area),
|
_ if HistoryUi::accepts(route) => HistoryUi::draw(f, app, content_area),
|
||||||
_ if RootFoldersUi::accepts(route) => RootFoldersUi::draw(f, app, content_area),
|
_ if RootFoldersUi::accepts(route) => RootFoldersUi::draw(f, app, content_area),
|
||||||
_ if IndexersUi::accepts(route) => IndexersUi::draw(f, app, content_area),
|
_ if IndexersUi::accepts(route) => IndexersUi::draw(f, app, content_area),
|
||||||
|
_ if SystemUi::accepts(route) => SystemUi::draw(f, app, content_area),
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@ source: src/ui/lidarr_ui/lidarr_ui_tests.rs
|
|||||||
expression: output
|
expression: output
|
||||||
---
|
---
|
||||||
╭ Artists ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
╭ Artists ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
│ Library │ Downloads │ History │ Root Folders │ Indexers │
|
│ Library │ Downloads │ History │ Root Folders │ Indexers │ System │
|
||||||
│───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
|
│───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
|
||||||
│ Name ▼ Type Status Quality Profile Metadata Profile Albums Tracks Size Monitored Tags │
|
│ Name ▼ Type Status Quality Profile Metadata Profile Albums Tracks Size Monitored Tags │
|
||||||
│=> Alex Person Continuing Lossless Standard 1 15/15 0.00 GB 🏷 alex │
|
│=> Alex Person Continuing Lossless Standard 1 15/15 0.00 GB 🏷 alex │
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@ source: src/ui/lidarr_ui/lidarr_ui_tests.rs
|
|||||||
expression: output
|
expression: output
|
||||||
---
|
---
|
||||||
╭ Artists ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
╭ Artists ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
│ Library │ Downloads │ History │ Root Folders │ Indexers │
|
│ Library │ Downloads │ History │ Root Folders │ Indexers │ System │
|
||||||
│───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
|
│───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
|
||||||
│ Title Percent Complete Size Output Path Indexer Download Client │
|
│ Title Percent Complete Size Output Path Indexer Download Client │
|
||||||
│=> Test download title 50% 3.30 GB /nfs/music/alex/album kickass torrents transmission │
|
│=> Test download title 50% 3.30 GB /nfs/music/alex/album kickass torrents transmission │
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@ source: src/ui/lidarr_ui/lidarr_ui_tests.rs
|
|||||||
expression: output
|
expression: output
|
||||||
---
|
---
|
||||||
╭ Artists ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
╭ Artists ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
│ Library │ Downloads │ History │ Root Folders │ Indexers │
|
│ Library │ Downloads │ History │ Root Folders │ Indexers │ System │
|
||||||
│───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
|
│───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
|
||||||
│ Source Title ▼ Event Type Quality Date │
|
│ Source Title ▼ Event Type Quality Date │
|
||||||
│=> Test source title grabbed Lossless 2023-01-01 00:00:00 UTC │
|
│=> Test source title grabbed Lossless 2023-01-01 00:00:00 UTC │
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@ source: src/ui/lidarr_ui/lidarr_ui_tests.rs
|
|||||||
expression: output
|
expression: output
|
||||||
---
|
---
|
||||||
╭ Artists ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
╭ Artists ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
│ Library │ Downloads │ History │ Root Folders │ Indexers │
|
│ Library │ Downloads │ History │ Root Folders │ Indexers │ System │
|
||||||
│───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
|
│───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
|
||||||
│ Indexer RSS Automatic Search Interactive Search Priority Tags │
|
│ Indexer RSS Automatic Search Interactive Search Priority Tags │
|
||||||
│=> Test Indexer Enabled Enabled Enabled 25 alex │
|
│=> Test Indexer Enabled Enabled Enabled 25 alex │
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@ source: src/ui/lidarr_ui/lidarr_ui_tests.rs
|
|||||||
expression: output
|
expression: output
|
||||||
---
|
---
|
||||||
╭ Artists ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
╭ Artists ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
│ Library │ Downloads │ History │ Root Folders │ Indexers │
|
│ Library │ Downloads │ History │ Root Folders │ Indexers │ System │
|
||||||
│───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
|
│───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
|
||||||
│ Path Free Space Unmapped Folders │
|
│ Path Free Space Unmapped Folders │
|
||||||
│=> /nfs 204800.00 GB 0 │
|
│=> /nfs 204800.00 GB 0 │
|
||||||
|
|||||||
@@ -0,0 +1,204 @@
|
|||||||
|
use crate::ui::styles::default_style;
|
||||||
|
use std::ops::Sub;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
use crate::ui::ui_test_utils::test_utils::Utc;
|
||||||
|
#[cfg(not(test))]
|
||||||
|
use chrono::Utc;
|
||||||
|
use ratatui::layout::Layout;
|
||||||
|
use ratatui::text::{Span, Text};
|
||||||
|
use ratatui::widgets::{Cell, Row};
|
||||||
|
use ratatui::{
|
||||||
|
Frame,
|
||||||
|
layout::{Constraint, Rect},
|
||||||
|
widgets::ListItem,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::app::App;
|
||||||
|
use crate::models::lidarr_models::LidarrTask;
|
||||||
|
use crate::models::servarr_data::lidarr::lidarr_data::ActiveLidarrBlock;
|
||||||
|
use crate::models::servarr_models::QueueEvent;
|
||||||
|
use crate::ui::lidarr_ui::system::system_details_ui::SystemDetailsUi;
|
||||||
|
use crate::ui::styles::ManagarrStyle;
|
||||||
|
use crate::ui::utils::{convert_to_minutes_hours_days, style_log_list_item};
|
||||||
|
use crate::ui::widgets::loading_block::LoadingBlock;
|
||||||
|
use crate::ui::widgets::managarr_table::ManagarrTable;
|
||||||
|
use crate::ui::widgets::selectable_list::SelectableList;
|
||||||
|
use crate::{
|
||||||
|
models::Route,
|
||||||
|
ui::{DrawUi, utils::title_block},
|
||||||
|
};
|
||||||
|
|
||||||
|
mod system_details_ui;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[path = "system_ui_tests.rs"]
|
||||||
|
mod system_ui_tests;
|
||||||
|
|
||||||
|
pub(super) const TASK_TABLE_HEADERS: [&str; 4] =
|
||||||
|
["Name", "Interval", "Last Execution", "Next Execution"];
|
||||||
|
|
||||||
|
pub(super) const TASK_TABLE_CONSTRAINTS: [Constraint; 4] = [
|
||||||
|
Constraint::Percentage(30),
|
||||||
|
Constraint::Percentage(23),
|
||||||
|
Constraint::Percentage(23),
|
||||||
|
Constraint::Percentage(23),
|
||||||
|
];
|
||||||
|
|
||||||
|
pub(super) struct SystemUi;
|
||||||
|
|
||||||
|
impl DrawUi for SystemUi {
|
||||||
|
fn accepts(route: Route) -> bool {
|
||||||
|
let Route::Lidarr(active_lidarr_block, _) = route else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
SystemDetailsUi::accepts(route) || active_lidarr_block == ActiveLidarrBlock::System
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||||
|
let route = app.get_current_route();
|
||||||
|
draw_system_ui_layout(f, app, area);
|
||||||
|
|
||||||
|
if SystemDetailsUi::accepts(route) {
|
||||||
|
SystemDetailsUi::draw(f, app, area);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_system_ui_layout(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||||
|
let [activities_area, logs_area] =
|
||||||
|
Layout::vertical([Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)]).areas(area);
|
||||||
|
|
||||||
|
let [tasks_area, events_area] =
|
||||||
|
Layout::horizontal([Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)]).areas(activities_area);
|
||||||
|
|
||||||
|
draw_tasks(f, app, tasks_area);
|
||||||
|
draw_queued_events(f, app, events_area);
|
||||||
|
draw_logs(f, app, logs_area);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_tasks(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||||
|
let tasks_row_mapping = |task: &LidarrTask| {
|
||||||
|
let task_props = extract_task_props(task);
|
||||||
|
|
||||||
|
Row::new(vec![
|
||||||
|
Cell::from(task_props.name),
|
||||||
|
Cell::from(task_props.interval),
|
||||||
|
Cell::from(task_props.last_execution),
|
||||||
|
Cell::from(task_props.next_execution),
|
||||||
|
])
|
||||||
|
.primary()
|
||||||
|
};
|
||||||
|
let tasks_table = ManagarrTable::new(Some(&mut app.data.lidarr_data.tasks), tasks_row_mapping)
|
||||||
|
.block(title_block("Tasks"))
|
||||||
|
.loading(app.is_loading)
|
||||||
|
.highlight_rows(false)
|
||||||
|
.headers(TASK_TABLE_HEADERS)
|
||||||
|
.constraints(TASK_TABLE_CONSTRAINTS);
|
||||||
|
|
||||||
|
f.render_widget(tasks_table, area);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn draw_queued_events(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||||
|
let events_row_mapping = |event: &QueueEvent| {
|
||||||
|
let queued = convert_to_minutes_hours_days(Utc::now().sub(event.queued).num_minutes());
|
||||||
|
let queued_string = if queued != "now" {
|
||||||
|
format!("{queued} ago")
|
||||||
|
} else {
|
||||||
|
queued
|
||||||
|
};
|
||||||
|
let started_string = if event.started.is_some() {
|
||||||
|
let started =
|
||||||
|
convert_to_minutes_hours_days(Utc::now().sub(event.started.unwrap()).num_minutes());
|
||||||
|
|
||||||
|
if started != "now" {
|
||||||
|
format!("{started} ago")
|
||||||
|
} else {
|
||||||
|
started
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
|
};
|
||||||
|
|
||||||
|
let duration = if event.duration.is_some() {
|
||||||
|
&event.duration.as_ref().unwrap()[..8]
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
};
|
||||||
|
|
||||||
|
Row::new(vec![
|
||||||
|
Cell::from(event.trigger.clone()),
|
||||||
|
Cell::from(event.status.clone()),
|
||||||
|
Cell::from(event.command_name.clone()),
|
||||||
|
Cell::from(queued_string),
|
||||||
|
Cell::from(started_string),
|
||||||
|
Cell::from(duration.to_owned()),
|
||||||
|
])
|
||||||
|
.primary()
|
||||||
|
};
|
||||||
|
let events_table = ManagarrTable::new(
|
||||||
|
Some(&mut app.data.lidarr_data.queued_events),
|
||||||
|
events_row_mapping,
|
||||||
|
)
|
||||||
|
.block(title_block("Queued Events"))
|
||||||
|
.loading(app.is_loading)
|
||||||
|
.highlight_rows(false)
|
||||||
|
.headers(["Trigger", "Status", "Name", "Queued", "Started", "Duration"])
|
||||||
|
.constraints([
|
||||||
|
Constraint::Percentage(13),
|
||||||
|
Constraint::Percentage(13),
|
||||||
|
Constraint::Percentage(30),
|
||||||
|
Constraint::Percentage(16),
|
||||||
|
Constraint::Percentage(14),
|
||||||
|
Constraint::Percentage(14),
|
||||||
|
]);
|
||||||
|
|
||||||
|
f.render_widget(events_table, area);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_logs(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||||
|
let block = title_block("Logs");
|
||||||
|
|
||||||
|
if app.data.lidarr_data.logs.items.is_empty() {
|
||||||
|
f.render_widget(LoadingBlock::new(app.is_loading, block), area);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let logs_box = SelectableList::new(&mut app.data.lidarr_data.logs, |log| {
|
||||||
|
let log_line = log.to_string();
|
||||||
|
let level = log_line.split('|').collect::<Vec<&str>>()[1].to_string();
|
||||||
|
|
||||||
|
style_log_list_item(ListItem::new(Text::from(Span::raw(log_line))), level)
|
||||||
|
})
|
||||||
|
.block(block)
|
||||||
|
.highlight_style(default_style());
|
||||||
|
|
||||||
|
f.render_widget(logs_box, area);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) struct TaskProps {
|
||||||
|
pub(super) name: String,
|
||||||
|
pub(super) interval: String,
|
||||||
|
pub(super) last_execution: String,
|
||||||
|
pub(super) next_execution: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn extract_task_props(task: &LidarrTask) -> TaskProps {
|
||||||
|
let interval = convert_to_minutes_hours_days(task.interval);
|
||||||
|
let next_execution =
|
||||||
|
convert_to_minutes_hours_days((task.next_execution - Utc::now()).num_minutes());
|
||||||
|
let last_execution =
|
||||||
|
convert_to_minutes_hours_days((Utc::now() - task.last_execution).num_minutes());
|
||||||
|
let last_execution_string = if last_execution != "now" {
|
||||||
|
format!("{last_execution} ago")
|
||||||
|
} else {
|
||||||
|
last_execution
|
||||||
|
};
|
||||||
|
|
||||||
|
TaskProps {
|
||||||
|
name: task.name.clone(),
|
||||||
|
interval,
|
||||||
|
last_execution: last_execution_string,
|
||||||
|
next_execution,
|
||||||
|
}
|
||||||
|
}
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
---
|
||||||
|
source: src/ui/lidarr_ui/system/system_details_ui_tests.rs
|
||||||
|
expression: output
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
╭ Log Details ───────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
+44
@@ -0,0 +1,44 @@
|
|||||||
|
---
|
||||||
|
source: src/ui/lidarr_ui/system/system_details_ui_tests.rs
|
||||||
|
expression: output
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
╭ Queued Events ────────────────────────────────────────────────────────────────────────────────╮
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
---
|
||||||
|
source: src/ui/lidarr_ui/system/system_details_ui_tests.rs
|
||||||
|
expression: output
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
╭ Tasks ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
---
|
||||||
|
source: src/ui/lidarr_ui/system/system_details_ui_tests.rs
|
||||||
|
expression: output
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
╭ Log Details ───────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ Loading ... │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
+44
@@ -0,0 +1,44 @@
|
|||||||
|
---
|
||||||
|
source: src/ui/lidarr_ui/system/system_details_ui_tests.rs
|
||||||
|
expression: output
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
╭ Queued Events ────────────────────────────────────────────────────────────────────────────────╮
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ Loading ... │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
---
|
||||||
|
source: src/ui/lidarr_ui/system/system_details_ui_tests.rs
|
||||||
|
expression: output
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
╭ Tasks ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ Loading ... │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
---
|
||||||
|
source: src/ui/lidarr_ui/system/system_details_ui_tests.rs
|
||||||
|
expression: output
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
╭ Updates ───────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ Loading ... │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
---
|
||||||
|
source: src/ui/lidarr_ui/system/system_details_ui_tests.rs
|
||||||
|
expression: output
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
╭ Log Details ───────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
|
│2025-12-16 16:40:59 UTC|INFO|ImportListSyncService|No list items to process │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
+44
@@ -0,0 +1,44 @@
|
|||||||
|
---
|
||||||
|
source: src/ui/lidarr_ui/system/system_details_ui_tests.rs
|
||||||
|
expression: output
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
╭ Queued Events ────────────────────────────────────────────────────────────────────────────────╮
|
||||||
|
│Trigger Status Name Queued Started Duration │
|
||||||
|
│manual completed Refresh Monitored Downlo 4 minutes ago 4 minutes ago 00:03:03 │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
---
|
||||||
|
source: src/ui/lidarr_ui/system/system_details_ui_tests.rs
|
||||||
|
expression: output
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
╭ Tasks ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
|
│ Name Interval Last Execution Next Execution │
|
||||||
|
│=> Backup 1 hour now 59 minutes │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ ╭────────────────────── Start Task ───────────────────────╮ │
|
||||||
|
│ │ Do you want to manually start this task: Backup? │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │╭────────────────────────────╮╭───────────────────────────╮│ │
|
||||||
|
│ ││ Yes ││ No ││ │
|
||||||
|
│ │╰────────────────────────────╯╰───────────────────────────╯│ │
|
||||||
|
│ ╰───────────────────────────────────────────────────────────╯ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
---
|
||||||
|
source: src/ui/lidarr_ui/system/system_details_ui_tests.rs
|
||||||
|
expression: output
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
╭ Tasks ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
|
│ Name Interval Last Execution Next Execution │
|
||||||
|
│=> Backup 1 hour now 59 minutes │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
---
|
||||||
|
source: src/ui/lidarr_ui/system/system_details_ui_tests.rs
|
||||||
|
expression: output
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
╭ Updates ───────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
|
│The latest version of Sonarr is already installed │
|
||||||
|
│ │
|
||||||
|
│4.3.2.1 - 2023-04-15 02:02:53 UTC (Currently Installed) │
|
||||||
|
│--------------------------------------------------------------------------------------------------------------------------│
|
||||||
|
│New: │
|
||||||
|
│ * Cool new thing │
|
||||||
|
│Fixed: │
|
||||||
|
│ * Some bugs killed │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│3.2.1.0 - 2023-04-15 02:02:53 UTC (Previously Installed) │
|
||||||
|
│--------------------------------------------------------------------------------------------------------------------------│
|
||||||
|
│New: │
|
||||||
|
│ * Cool new thing (old) │
|
||||||
|
│ * Other cool new thing (old) │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│2.1.0 - 2023-04-15 02:02:53 UTC │
|
||||||
|
│--------------------------------------------------------------------------------------------------------------------------│
|
||||||
|
│Fixed: │
|
||||||
|
│ * Killed bug 1 │
|
||||||
|
│ * Fixed bug 2 │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
---
|
||||||
|
source: src/ui/lidarr_ui/system/system_details_ui_tests.rs
|
||||||
|
expression: output
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
╭ Updates ───────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ Loading ... │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
+54
@@ -0,0 +1,54 @@
|
|||||||
|
---
|
||||||
|
source: src/ui/lidarr_ui/system/system_ui_tests.rs
|
||||||
|
expression: output
|
||||||
|
---
|
||||||
|
╭ Tasks ────────────────────────────────────────────────────────────────────────╮╭ Queued Events ───────────────────────────────────────────────────────────────╮
|
||||||
|
│Name Interval Last Execution Next Execution ││Trigger Status Name Queued Started Duration │
|
||||||
|
│Backup 1 hour now 59 minutes ││manual completed Refresh Monitored D 4 minutes ago 4 minutes a 00:03:03 │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ╭ Log Details ───────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │
|
||||||
|
│ │2025-12-16 16:40:59 UTC|INFO|ImportListSyncService|No list items to process │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
╰───────────────────│ │────────────────────╯
|
||||||
|
╭ Logs ───────────│ │────────────────────╮
|
||||||
|
│2025-12-16 16:40:59│ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
+54
@@ -0,0 +1,54 @@
|
|||||||
|
---
|
||||||
|
source: src/ui/lidarr_ui/system/system_ui_tests.rs
|
||||||
|
expression: output
|
||||||
|
---
|
||||||
|
╭ Tasks ────────────────────────────────────────────────────────────────────────╮╭ Queued Events ───────────────────────────────────────────────────────────────╮
|
||||||
|
│Name Interval Last Execution Next Execution ││Trigger Status Name Queued Started Duration │
|
||||||
|
│Backup 1 hour now 59 minutes ││manual completed Refresh Monitored D 4 minutes ago 4 minutes a 00:03:03 │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ╭ Queued Events ────────────────────────────────────────────────────────────────────────────────╮ │
|
||||||
|
│ │Trigger Status Name Queued Started Duration │ │
|
||||||
|
│ │manual completed Refresh Monitored Downlo 4 minutes ago 4 minutes ago 00:03:03 │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
╰────────────────────────────────│ │────────────────────────────────╯
|
||||||
|
╭ Logs ────────────────────────│ │────────────────────────────────╮
|
||||||
|
│2025-12-16 16:40:59 UTC|INFO|Imp│ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ ╰─────────────────────────────────────────────────────────────────────────────────────────────────╯ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
+54
@@ -0,0 +1,54 @@
|
|||||||
|
---
|
||||||
|
source: src/ui/lidarr_ui/system/system_ui_tests.rs
|
||||||
|
expression: output
|
||||||
|
---
|
||||||
|
╭ Tasks ────────────────────────────────────────────────────────────────────────╮╭ Queued Events ───────────────────────────────────────────────────────────────╮
|
||||||
|
│Name Interval Last Execution Next Execution ││Trigger Status Name Queued Started Duration │
|
||||||
|
│Backup 1 hour now 59 minutes ││manual completed Refresh Monitored D 4 minutes ago 4 minutes a 00:03:03 │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ╭ Tasks ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │
|
||||||
|
│ │ Name Interval Last Execution Next Execution │ │
|
||||||
|
│ │=> Backup 1 hour now 59 minutes │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ ╭────────────────────── Start Task ───────────────────────╮ │ │
|
||||||
|
│ │ │ Do you want to manually start this task: Backup? │ │ │
|
||||||
|
│ │ │ │ │ │
|
||||||
|
│ │ │ │ │ │
|
||||||
|
│ │ │ │ │ │
|
||||||
|
│ │ │ │ │ │
|
||||||
|
│ │ │ │ │ │
|
||||||
|
│ │ │ │ │ │
|
||||||
|
╰───────────────────│ │ │ │────────────────────╯
|
||||||
|
╭ Logs ───────────│ │ │ │────────────────────╮
|
||||||
|
│2025-12-16 16:40:59│ │ │ │ │
|
||||||
|
│ │ │ │ │ │
|
||||||
|
│ │ │ │ │ │
|
||||||
|
│ │ │ │ │ │
|
||||||
|
│ │ │╭────────────────────────────╮╭───────────────────────────╮│ │ │
|
||||||
|
│ │ ││ Yes ││ No ││ │ │
|
||||||
|
│ │ │╰────────────────────────────╯╰───────────────────────────╯│ │ │
|
||||||
|
│ │ ╰───────────────────────────────────────────────────────────╯ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
+54
@@ -0,0 +1,54 @@
|
|||||||
|
---
|
||||||
|
source: src/ui/lidarr_ui/system/system_ui_tests.rs
|
||||||
|
expression: output
|
||||||
|
---
|
||||||
|
╭ Tasks ────────────────────────────────────────────────────────────────────────╮╭ Queued Events ───────────────────────────────────────────────────────────────╮
|
||||||
|
│Name Interval Last Execution Next Execution ││Trigger Status Name Queued Started Duration │
|
||||||
|
│Backup 1 hour now 59 minutes ││manual completed Refresh Monitored D 4 minutes ago 4 minutes a 00:03:03 │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ╭ Tasks ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │
|
||||||
|
│ │ Name Interval Last Execution Next Execution │ │
|
||||||
|
│ │=> Backup 1 hour now 59 minutes │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
╰───────────────────│ │────────────────────╯
|
||||||
|
╭ Logs ───────────│ │────────────────────╮
|
||||||
|
│2025-12-16 16:40:59│ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
+54
@@ -0,0 +1,54 @@
|
|||||||
|
---
|
||||||
|
source: src/ui/lidarr_ui/system/system_ui_tests.rs
|
||||||
|
expression: output
|
||||||
|
---
|
||||||
|
╭ Tasks ────────────────────────────────────────────────────────────────────────╮╭ Queued Events ───────────────────────────────────────────────────────────────╮
|
||||||
|
│Name Interval Last Execution Next Execution ││Trigger Status Name Queued Started Duration │
|
||||||
|
│Backup 1 hour now 59 minutes ││manual completed Refresh Monitored D 4 minutes ago 4 minutes a 00:03:03 │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ╭ Updates ───────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │
|
||||||
|
│ │The latest version of Sonarr is already installed │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │4.3.2.1 - 2023-04-15 02:02:53 UTC (Currently Installed) │ │
|
||||||
|
│ │--------------------------------------------------------------------------------------------------------------------------│ │
|
||||||
|
│ │New: │ │
|
||||||
|
│ │ * Cool new thing │ │
|
||||||
|
│ │Fixed: │ │
|
||||||
|
│ │ * Some bugs killed │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │3.2.1.0 - 2023-04-15 02:02:53 UTC (Previously Installed) │ │
|
||||||
|
│ │--------------------------------------------------------------------------------------------------------------------------│ │
|
||||||
|
│ │New: │ │
|
||||||
|
│ │ * Cool new thing (old) │ │
|
||||||
|
│ │ * Other cool new thing (old) │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
╰───────────────────│2.1.0 - 2023-04-15 02:02:53 UTC │────────────────────╯
|
||||||
|
╭ Logs ───────────│--------------------------------------------------------------------------------------------------------------------------│────────────────────╮
|
||||||
|
│2025-12-16 16:40:59│Fixed: │ │
|
||||||
|
│ │ * Killed bug 1 │ │
|
||||||
|
│ │ * Fixed bug 2 │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
+54
@@ -0,0 +1,54 @@
|
|||||||
|
---
|
||||||
|
source: src/ui/lidarr_ui/system/system_ui_tests.rs
|
||||||
|
expression: output
|
||||||
|
---
|
||||||
|
╭ Tasks ────────────────────────────────────────────────────────────────────────╮╭ Queued Events ───────────────────────────────────────────────────────────────╮
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ Loading ... ││ Loading ... │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
╰─────────────────────────────────────────────────────────────────────────────────╯╰────────────────────────────────────────────────────────────────────────────────╯
|
||||||
|
╭ Logs ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ Loading ... │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
+54
@@ -0,0 +1,54 @@
|
|||||||
|
---
|
||||||
|
source: src/ui/lidarr_ui/system/system_ui_tests.rs
|
||||||
|
expression: output
|
||||||
|
---
|
||||||
|
╭ Tasks ────────────────────────────────────────────────────────────────────────╮╭ Queued Events ───────────────────────────────────────────────────────────────╮
|
||||||
|
│Name Interval Last Execution Next Execution ││Trigger Status Name Queued Started Duration │
|
||||||
|
│Backup 1 hour now 59 minutes ││manual completed Refresh Monitored D 4 minutes ago 4 minutes a 00:03:03 │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
╰─────────────────────────────────────────────────────────────────────────────────╯╰────────────────────────────────────────────────────────────────────────────────╯
|
||||||
|
╭ Logs ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
|
│2025-12-16 16:40:59 UTC|INFO|ImportListSyncService|No list items to process │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
+54
@@ -0,0 +1,54 @@
|
|||||||
|
---
|
||||||
|
source: src/ui/lidarr_ui/system/system_ui_tests.rs
|
||||||
|
expression: output
|
||||||
|
---
|
||||||
|
╭ Tasks ────────────────────────────────────────────────────────────────────────╮╭ Queued Events ───────────────────────────────────────────────────────────────╮
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
╰─────────────────────────────────────────────────────────────────────────────────╯╰────────────────────────────────────────────────────────────────────────────────╯
|
||||||
|
╭ Logs ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
+54
@@ -0,0 +1,54 @@
|
|||||||
|
---
|
||||||
|
source: src/ui/lidarr_ui/system/system_ui_tests.rs
|
||||||
|
expression: output
|
||||||
|
---
|
||||||
|
╭ Tasks ────────────────────────────────────────────────────────────────────────╮╭ Queued Events ───────────────────────────────────────────────────────────────╮
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ Loading ... ││ Loading ... │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
╰─────────────────────────────────────────────────────────────────────────────────╯╰────────────────────────────────────────────────────────────────────────────────╯
|
||||||
|
╭ Logs ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ Loading ... │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
+54
@@ -0,0 +1,54 @@
|
|||||||
|
---
|
||||||
|
source: src/ui/lidarr_ui/system/system_ui_tests.rs
|
||||||
|
expression: output
|
||||||
|
---
|
||||||
|
╭ Tasks ────────────────────────────────────────────────────────────────────────╮╭ Queued Events ───────────────────────────────────────────────────────────────╮
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ Loading ... ││ Loading ... │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
│ ││ │
|
||||||
|
╰─────────────────────────────────────────────────────────────────────────────────╯╰────────────────────────────────────────────────────────────────────────────────╯
|
||||||
|
╭ Logs ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
|
│2025-12-16 16:40:59 UTC|INFO|ImportListSyncService|No list items to process │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
@@ -0,0 +1,144 @@
|
|||||||
|
use ratatui::Frame;
|
||||||
|
use ratatui::layout::Rect;
|
||||||
|
use ratatui::text::{Span, Text};
|
||||||
|
use ratatui::widgets::{Cell, ListItem, Paragraph, Row};
|
||||||
|
|
||||||
|
use crate::app::App;
|
||||||
|
use crate::models::Route;
|
||||||
|
use crate::models::lidarr_models::LidarrTask;
|
||||||
|
use crate::models::servarr_data::lidarr::lidarr_data::{ActiveLidarrBlock, SYSTEM_DETAILS_BLOCKS};
|
||||||
|
use crate::ui::lidarr_ui::system::{
|
||||||
|
TASK_TABLE_CONSTRAINTS, TASK_TABLE_HEADERS, draw_queued_events, extract_task_props,
|
||||||
|
};
|
||||||
|
use crate::ui::styles::ManagarrStyle;
|
||||||
|
use crate::ui::utils::{borderless_block, style_log_list_item, title_block};
|
||||||
|
use crate::ui::widgets::confirmation_prompt::ConfirmationPrompt;
|
||||||
|
use crate::ui::widgets::loading_block::LoadingBlock;
|
||||||
|
use crate::ui::widgets::managarr_table::ManagarrTable;
|
||||||
|
use crate::ui::widgets::popup::{Popup, Size};
|
||||||
|
use crate::ui::widgets::selectable_list::SelectableList;
|
||||||
|
use crate::ui::{DrawUi, draw_popup};
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[path = "system_details_ui_tests.rs"]
|
||||||
|
mod system_details_ui_tests;
|
||||||
|
|
||||||
|
pub(super) struct SystemDetailsUi;
|
||||||
|
|
||||||
|
impl DrawUi for SystemDetailsUi {
|
||||||
|
fn accepts(route: Route) -> bool {
|
||||||
|
let Route::Lidarr(active_lidarr_block, _) = route else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
SYSTEM_DETAILS_BLOCKS.contains(&active_lidarr_block)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(f: &mut Frame<'_>, app: &mut App<'_>, _area: Rect) {
|
||||||
|
if let Route::Lidarr(active_lidarr_block, _) = app.get_current_route() {
|
||||||
|
match active_lidarr_block {
|
||||||
|
ActiveLidarrBlock::SystemLogs => {
|
||||||
|
draw_logs_popup(f, app);
|
||||||
|
}
|
||||||
|
ActiveLidarrBlock::SystemTasks | ActiveLidarrBlock::SystemTaskStartConfirmPrompt => {
|
||||||
|
draw_popup(f, app, draw_tasks_popup, Size::Large)
|
||||||
|
}
|
||||||
|
ActiveLidarrBlock::SystemQueuedEvents => {
|
||||||
|
draw_popup(f, app, draw_queued_events, Size::Medium)
|
||||||
|
}
|
||||||
|
ActiveLidarrBlock::SystemUpdates => {
|
||||||
|
draw_updates_popup(f, app);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_logs_popup(f: &mut Frame<'_>, app: &mut App<'_>) {
|
||||||
|
let block = title_block("Log Details");
|
||||||
|
|
||||||
|
if app.data.lidarr_data.log_details.items.is_empty() {
|
||||||
|
let loading = LoadingBlock::new(app.is_loading, borderless_block());
|
||||||
|
let popup = Popup::new(loading).size(Size::Large).block(block).margin(1);
|
||||||
|
|
||||||
|
f.render_widget(popup, f.area());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let logs_list = SelectableList::new(&mut app.data.lidarr_data.log_details, |log| {
|
||||||
|
let log_line = log.to_string();
|
||||||
|
let level = log.text.split('|').collect::<Vec<&str>>()[1].to_string();
|
||||||
|
|
||||||
|
style_log_list_item(ListItem::new(Text::from(Span::raw(log_line))), level)
|
||||||
|
})
|
||||||
|
.block(borderless_block());
|
||||||
|
let popup = Popup::new(logs_list)
|
||||||
|
.size(Size::Large)
|
||||||
|
.block(block)
|
||||||
|
.margin(1);
|
||||||
|
|
||||||
|
f.render_widget(popup, f.area());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_tasks_popup(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||||
|
let tasks_row_mapping = |task: &LidarrTask| {
|
||||||
|
let task_props = extract_task_props(task);
|
||||||
|
|
||||||
|
Row::new(vec![
|
||||||
|
Cell::from(task_props.name),
|
||||||
|
Cell::from(task_props.interval),
|
||||||
|
Cell::from(task_props.last_execution),
|
||||||
|
Cell::from(task_props.next_execution),
|
||||||
|
])
|
||||||
|
.primary()
|
||||||
|
};
|
||||||
|
let tasks_table = ManagarrTable::new(Some(&mut app.data.lidarr_data.tasks), tasks_row_mapping)
|
||||||
|
.loading(app.is_loading)
|
||||||
|
.margin(1)
|
||||||
|
.headers(TASK_TABLE_HEADERS)
|
||||||
|
.constraints(TASK_TABLE_CONSTRAINTS);
|
||||||
|
|
||||||
|
f.render_widget(title_block("Tasks"), area);
|
||||||
|
f.render_widget(tasks_table, area);
|
||||||
|
|
||||||
|
if matches!(
|
||||||
|
app.get_current_route(),
|
||||||
|
Route::Lidarr(ActiveLidarrBlock::SystemTaskStartConfirmPrompt, _)
|
||||||
|
) {
|
||||||
|
let prompt = format!(
|
||||||
|
"Do you want to manually start this task: {}?",
|
||||||
|
app.data.lidarr_data.tasks.current_selection().name
|
||||||
|
);
|
||||||
|
let confirmation_prompt = ConfirmationPrompt::new()
|
||||||
|
.title("Start Task")
|
||||||
|
.prompt(&prompt)
|
||||||
|
.yes_no_value(app.data.lidarr_data.prompt_confirm);
|
||||||
|
|
||||||
|
f.render_widget(
|
||||||
|
Popup::new(confirmation_prompt).size(Size::MediumPrompt),
|
||||||
|
f.area(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_updates_popup(f: &mut Frame<'_>, app: &mut App<'_>) {
|
||||||
|
let updates = app.data.lidarr_data.updates.get_text();
|
||||||
|
let block = title_block("Updates");
|
||||||
|
|
||||||
|
if !updates.is_empty() && !app.is_loading {
|
||||||
|
let updates_paragraph = Paragraph::new(Text::from(updates))
|
||||||
|
.block(borderless_block())
|
||||||
|
.scroll((app.data.lidarr_data.updates.offset, 0));
|
||||||
|
let popup = Popup::new(updates_paragraph)
|
||||||
|
.size(Size::Large)
|
||||||
|
.block(block)
|
||||||
|
.margin(1);
|
||||||
|
|
||||||
|
f.render_widget(popup, f.area());
|
||||||
|
} else {
|
||||||
|
let loading = LoadingBlock::new(app.is_loading, borderless_block());
|
||||||
|
let popup = Popup::new(loading).size(Size::Large).block(block).margin(1);
|
||||||
|
|
||||||
|
f.render_widget(popup, f.area());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use strum::IntoEnumIterator;
|
||||||
|
|
||||||
|
use crate::app::App;
|
||||||
|
use crate::models::servarr_data::lidarr::lidarr_data::{
|
||||||
|
ActiveLidarrBlock, SYSTEM_DETAILS_BLOCKS,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::ui::DrawUi;
|
||||||
|
use crate::ui::lidarr_ui::system::system_details_ui::SystemDetailsUi;
|
||||||
|
use crate::ui::ui_test_utils::test_utils::render_to_string_with_app;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_system_details_ui_accepts() {
|
||||||
|
ActiveLidarrBlock::iter().for_each(|active_lidarr_block| {
|
||||||
|
if SYSTEM_DETAILS_BLOCKS.contains(&active_lidarr_block) {
|
||||||
|
assert!(SystemDetailsUi::accepts(active_lidarr_block.into()));
|
||||||
|
} else {
|
||||||
|
assert!(!SystemDetailsUi::accepts(active_lidarr_block.into()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
mod snapshot_tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::models::ScrollableText;
|
||||||
|
use crate::ui::ui_test_utils::test_utils::TerminalSize;
|
||||||
|
use rstest::rstest;
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn test_system_details_ui_popups(
|
||||||
|
#[values(
|
||||||
|
ActiveLidarrBlock::SystemLogs,
|
||||||
|
ActiveLidarrBlock::SystemQueuedEvents,
|
||||||
|
ActiveLidarrBlock::SystemTasks,
|
||||||
|
ActiveLidarrBlock::SystemTaskStartConfirmPrompt,
|
||||||
|
ActiveLidarrBlock::SystemUpdates
|
||||||
|
)]
|
||||||
|
active_lidarr_block: ActiveLidarrBlock,
|
||||||
|
) {
|
||||||
|
let mut app = App::test_default_fully_populated();
|
||||||
|
app.push_navigation_stack(active_lidarr_block.into());
|
||||||
|
|
||||||
|
let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| {
|
||||||
|
SystemDetailsUi::draw(f, app, f.area());
|
||||||
|
});
|
||||||
|
|
||||||
|
insta::assert_snapshot!(format!("popup_{active_lidarr_block}"), output);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn test_system_details_ui_loading(
|
||||||
|
#[values(
|
||||||
|
ActiveLidarrBlock::SystemLogs,
|
||||||
|
ActiveLidarrBlock::SystemQueuedEvents,
|
||||||
|
ActiveLidarrBlock::SystemTasks,
|
||||||
|
ActiveLidarrBlock::SystemUpdates
|
||||||
|
)]
|
||||||
|
active_lidarr_block: ActiveLidarrBlock,
|
||||||
|
) {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.is_loading = true;
|
||||||
|
app.push_navigation_stack(active_lidarr_block.into());
|
||||||
|
|
||||||
|
let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| {
|
||||||
|
SystemDetailsUi::draw(f, app, f.area());
|
||||||
|
});
|
||||||
|
|
||||||
|
insta::assert_snapshot!(format!("loading_{active_lidarr_block}"), output);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_system_details_ui_updates_popup_loading_when_empty() {
|
||||||
|
let mut app = App::test_default_fully_populated();
|
||||||
|
app.is_loading = true;
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::SystemUpdates.into());
|
||||||
|
app.data.lidarr_data.updates = ScrollableText::with_string("".to_string());
|
||||||
|
|
||||||
|
let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| {
|
||||||
|
SystemDetailsUi::draw(f, app, f.area());
|
||||||
|
});
|
||||||
|
|
||||||
|
insta::assert_snapshot!(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn test_system_details_ui_popups_empty(
|
||||||
|
#[values(
|
||||||
|
ActiveLidarrBlock::SystemLogs,
|
||||||
|
ActiveLidarrBlock::SystemQueuedEvents,
|
||||||
|
ActiveLidarrBlock::SystemTasks
|
||||||
|
)]
|
||||||
|
active_lidarr_block: ActiveLidarrBlock,
|
||||||
|
) {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.push_navigation_stack(active_lidarr_block.into());
|
||||||
|
|
||||||
|
let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| {
|
||||||
|
SystemDetailsUi::draw(f, app, f.area());
|
||||||
|
});
|
||||||
|
|
||||||
|
insta::assert_snapshot!(format!("empty_{active_lidarr_block}"), output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use strum::IntoEnumIterator;
|
||||||
|
|
||||||
|
use crate::app::App;
|
||||||
|
use crate::models::servarr_data::lidarr::lidarr_data::{
|
||||||
|
ActiveLidarrBlock, SYSTEM_DETAILS_BLOCKS,
|
||||||
|
};
|
||||||
|
use crate::ui::DrawUi;
|
||||||
|
use crate::ui::lidarr_ui::system::SystemUi;
|
||||||
|
use crate::ui::ui_test_utils::test_utils::render_to_string_with_app;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_system_ui_accepts() {
|
||||||
|
let mut system_ui_blocks = Vec::new();
|
||||||
|
system_ui_blocks.push(ActiveLidarrBlock::System);
|
||||||
|
system_ui_blocks.extend(SYSTEM_DETAILS_BLOCKS);
|
||||||
|
|
||||||
|
ActiveLidarrBlock::iter().for_each(|active_lidarr_block| {
|
||||||
|
if system_ui_blocks.contains(&active_lidarr_block) {
|
||||||
|
assert!(SystemUi::accepts(active_lidarr_block.into()));
|
||||||
|
} else {
|
||||||
|
assert!(!SystemUi::accepts(active_lidarr_block.into()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
mod snapshot_tests {
|
||||||
|
use crate::models::stateful_list::StatefulList;
|
||||||
|
use crate::ui::ui_test_utils::test_utils::TerminalSize;
|
||||||
|
use rstest::rstest;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_system_ui_renders_system_tab_loading() {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.is_loading = true;
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::System.into());
|
||||||
|
|
||||||
|
let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| {
|
||||||
|
SystemUi::draw(f, app, f.area());
|
||||||
|
});
|
||||||
|
|
||||||
|
insta::assert_snapshot!(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_system_ui_renders_logs_loading() {
|
||||||
|
let mut app = App::test_default_fully_populated();
|
||||||
|
app.is_loading = true;
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::System.into());
|
||||||
|
app.data.lidarr_data.logs = StatefulList::default();
|
||||||
|
|
||||||
|
let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| {
|
||||||
|
SystemUi::draw(f, app, f.area());
|
||||||
|
});
|
||||||
|
|
||||||
|
insta::assert_snapshot!(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_system_ui_renders_system_tab_task_and_events_loading() {
|
||||||
|
let mut app = App::test_default_fully_populated();
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::System.into());
|
||||||
|
app.is_loading = true;
|
||||||
|
|
||||||
|
let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| {
|
||||||
|
SystemUi::draw(f, app, f.area());
|
||||||
|
});
|
||||||
|
|
||||||
|
insta::assert_snapshot!(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_system_ui_renders_system_tab() {
|
||||||
|
let mut app = App::test_default_fully_populated();
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::System.into());
|
||||||
|
|
||||||
|
let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| {
|
||||||
|
SystemUi::draw(f, app, f.area());
|
||||||
|
});
|
||||||
|
|
||||||
|
insta::assert_snapshot!(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_system_ui_renders_system_tab_empty() {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::System.into());
|
||||||
|
|
||||||
|
let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| {
|
||||||
|
SystemUi::draw(f, app, f.area());
|
||||||
|
});
|
||||||
|
|
||||||
|
insta::assert_snapshot!(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn test_system_details_ui_renders_popups_over_system_ui(
|
||||||
|
#[values(
|
||||||
|
ActiveLidarrBlock::SystemLogs,
|
||||||
|
ActiveLidarrBlock::SystemQueuedEvents,
|
||||||
|
ActiveLidarrBlock::SystemTasks,
|
||||||
|
ActiveLidarrBlock::SystemTaskStartConfirmPrompt,
|
||||||
|
ActiveLidarrBlock::SystemUpdates
|
||||||
|
)]
|
||||||
|
active_lidarr_block: ActiveLidarrBlock,
|
||||||
|
) {
|
||||||
|
let mut app = App::test_default_fully_populated();
|
||||||
|
app.push_navigation_stack(active_lidarr_block.into());
|
||||||
|
|
||||||
|
let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| {
|
||||||
|
SystemUi::draw(f, app, f.area());
|
||||||
|
});
|
||||||
|
|
||||||
|
insta::assert_snapshot!(
|
||||||
|
format!("popups_over_system_ui_{active_lidarr_block}"),
|
||||||
|
output
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user