feat: Full support for deleting an artist via CLI and TUI

This commit is contained in:
2026-01-05 15:44:51 -07:00
parent bc3aeefa6e
commit 6771a0ab38
43 changed files with 1995 additions and 332 deletions
+238 -7
View File
@@ -2,19 +2,250 @@
mod tests {
use strum::IntoEnumIterator;
use crate::models::servarr_data::lidarr::lidarr_data::{ActiveLidarrBlock, LIBRARY_BLOCKS};
use crate::models::Route;
use crate::models::lidarr_models::{Artist, ArtistStatistics, ArtistStatus};
use crate::models::servarr_data::lidarr::lidarr_data::{
ActiveLidarrBlock, DELETE_ARTIST_BLOCKS, LIBRARY_BLOCKS,
};
use crate::ui::lidarr_ui::library::{LibraryUi, decorate_artist_row_with_style};
use crate::ui::styles::ManagarrStyle;
use crate::ui::DrawUi;
use crate::ui::lidarr_ui::library::LibraryUi;
use pretty_assertions::assert_eq;
use ratatui::widgets::{Cell, Row};
#[test]
fn test_library_ui_accepts() {
for lidarr_block in ActiveLidarrBlock::iter() {
if LIBRARY_BLOCKS.contains(&lidarr_block) {
assert!(LibraryUi::accepts(Route::Lidarr(lidarr_block, None)));
let mut library_ui_blocks = Vec::new();
library_ui_blocks.extend(LIBRARY_BLOCKS);
library_ui_blocks.extend(DELETE_ARTIST_BLOCKS);
for active_lidarr_block in ActiveLidarrBlock::iter() {
if library_ui_blocks.contains(&active_lidarr_block) {
assert!(LibraryUi::accepts(active_lidarr_block.into()));
} else {
assert!(!LibraryUi::accepts(Route::Lidarr(lidarr_block, None)));
assert!(!LibraryUi::accepts(active_lidarr_block.into()));
}
}
}
#[test]
fn test_decorate_row_with_style_unmonitored() {
let artist = Artist::default();
let row = Row::new(vec![Cell::from("test".to_owned())]);
let style = decorate_artist_row_with_style(&artist, row.clone());
assert_eq!(style, row.unmonitored());
}
#[test]
fn test_decorate_row_with_style_downloaded_when_ended_and_all_tracks_present() {
let artist = Artist {
monitored: true,
status: ArtistStatus::Ended,
statistics: Some(ArtistStatistics {
track_file_count: 10,
total_track_count: 10,
..ArtistStatistics::default()
}),
..Artist::default()
};
let row = Row::new(vec![Cell::from("test".to_owned())]);
let style = decorate_artist_row_with_style(&artist, row.clone());
assert_eq!(style, row.downloaded());
}
#[test]
fn test_decorate_row_with_style_missing_when_ended_and_tracks_are_missing() {
let artist = Artist {
monitored: true,
status: ArtistStatus::Ended,
statistics: Some(ArtistStatistics {
track_file_count: 5,
total_track_count: 10,
..ArtistStatistics::default()
}),
..Artist::default()
};
let row = Row::new(vec![Cell::from("test".to_owned())]);
let style = decorate_artist_row_with_style(&artist, row.clone());
assert_eq!(style, row.missing());
}
#[test]
fn test_decorate_row_with_style_indeterminate_when_ended_and_no_statistics() {
let artist = Artist {
monitored: true,
status: ArtistStatus::Ended,
statistics: None,
..Artist::default()
};
let row = Row::new(vec![Cell::from("test".to_owned())]);
let style = decorate_artist_row_with_style(&artist, row.clone());
assert_eq!(style, row.indeterminate());
}
#[test]
fn test_decorate_row_with_style_indeterminate_when_ended_and_total_track_count_is_zero() {
let artist = Artist {
monitored: true,
status: ArtistStatus::Ended,
statistics: Some(ArtistStatistics {
track_file_count: 0,
total_track_count: 0,
..ArtistStatistics::default()
}),
..Artist::default()
};
let row = Row::new(vec![Cell::from("test".to_owned())]);
let style = decorate_artist_row_with_style(&artist, row.clone());
assert_eq!(style, row.missing());
}
#[test]
fn test_decorate_row_with_style_unreleased_when_continuing_and_all_tracks_present() {
let artist = Artist {
monitored: true,
status: ArtistStatus::Continuing,
statistics: Some(ArtistStatistics {
track_file_count: 10,
total_track_count: 10,
..ArtistStatistics::default()
}),
..Artist::default()
};
let row = Row::new(vec![Cell::from("test".to_owned())]);
let style = decorate_artist_row_with_style(&artist, row.clone());
assert_eq!(style, row.unreleased());
}
#[test]
fn test_decorate_row_with_style_missing_when_continuing_and_tracks_are_missing() {
let artist = Artist {
monitored: true,
status: ArtistStatus::Continuing,
statistics: Some(ArtistStatistics {
track_file_count: 5,
total_track_count: 10,
..ArtistStatistics::default()
}),
..Artist::default()
};
let row = Row::new(vec![Cell::from("test".to_owned())]);
let style = decorate_artist_row_with_style(&artist, row.clone());
assert_eq!(style, row.missing());
}
#[test]
fn test_decorate_row_with_style_indeterminate_when_continuing_and_no_statistics() {
let artist = Artist {
monitored: true,
status: ArtistStatus::Continuing,
statistics: None,
..Artist::default()
};
let row = Row::new(vec![Cell::from("test".to_owned())]);
let style = decorate_artist_row_with_style(&artist, row.clone());
assert_eq!(style, row.indeterminate());
}
#[test]
fn test_decorate_row_with_style_defaults_to_indeterminate_for_deleted_status() {
let artist = Artist {
monitored: true,
status: ArtistStatus::Deleted,
..Artist::default()
};
let row = Row::new(vec![Cell::from("test".to_owned())]);
let style = decorate_artist_row_with_style(&artist, row.clone());
assert_eq!(style, row.indeterminate());
}
mod snapshot_tests {
use crate::app::App;
use crate::models::BlockSelectionState;
use crate::models::servarr_data::lidarr::lidarr_data::{
ActiveLidarrBlock, DELETE_ARTIST_SELECTION_BLOCKS,
};
use rstest::rstest;
use crate::ui::lidarr_ui::library::LibraryUi;
use crate::ui::ui_test_utils::test_utils::{TerminalSize, render_to_string_with_app};
use crate::ui::DrawUi;
#[rstest]
fn test_library_ui_renders(
#[values(
ActiveLidarrBlock::Artists,
ActiveLidarrBlock::ArtistsSortPrompt,
ActiveLidarrBlock::SearchArtists,
ActiveLidarrBlock::SearchArtistsError,
ActiveLidarrBlock::FilterArtists,
ActiveLidarrBlock::FilterArtistsError
)]
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| {
LibraryUi::draw(f, app, f.area());
});
insta::assert_snapshot!(format!("lidarr_library_{active_lidarr_block}"), output);
}
#[test]
fn test_library_ui_renders_loading() {
let mut app = App::test_default_fully_populated();
app.is_loading = true;
app.push_navigation_stack(ActiveLidarrBlock::Artists.into());
let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| {
LibraryUi::draw(f, app, f.area());
});
insta::assert_snapshot!(output);
}
#[test]
fn test_library_ui_renders_empty() {
let mut app = App::test_default();
app.push_navigation_stack(ActiveLidarrBlock::Artists.into());
let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| {
LibraryUi::draw(f, app, f.area());
});
insta::assert_snapshot!(output);
}
#[test]
fn test_library_ui_renders_delete_artist_over_library() {
let mut app = App::test_default_fully_populated();
app.push_navigation_stack(ActiveLidarrBlock::DeleteArtistPrompt.into());
app.data.lidarr_data.selected_block =
BlockSelectionState::new(DELETE_ARTIST_SELECTION_BLOCKS);
let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| {
LibraryUi::draw(f, app, f.area());
});
insta::assert_snapshot!(output);
}
}
}