From e9c323c4e86e421d9932a2b05efdfebbfd0746e7 Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Thu, 17 Sep 2026 15:30:16 -0600 Subject: [PATCH] test(sonarr): cover the episode row style branches decorate_with_row_style had no unit tests, so every one of its seven return sites could be swapped for another style without a failure. Add one test per branch, including the ordering cases where the download match precedes the monitored guard and the monitored guard precedes the air date check, using a local download fixture with distinct episode, other-episode, and download ids. --- .../library/season_details_ui_tests.rs | 249 +++++++++++++++++- 1 file changed, 248 insertions(+), 1 deletion(-) diff --git a/src/ui/sonarr_ui/library/season_details_ui_tests.rs b/src/ui/sonarr_ui/library/season_details_ui_tests.rs index 26cd5030..f5754713 100644 --- a/src/ui/sonarr_ui/library/season_details_ui_tests.rs +++ b/src/ui/sonarr_ui/library/season_details_ui_tests.rs @@ -1,14 +1,23 @@ #[cfg(test)] mod tests { + use chrono::{Duration, Utc}; + use pretty_assertions::assert_eq; + use ratatui::widgets::{Cell, Row}; + use serde_json::Number; use strum::IntoEnumIterator; use crate::app::App; use crate::models::servarr_data::sonarr::sonarr_data::{ ActiveSonarrBlock, EPISODE_DETAILS_BLOCKS, SEASON_DETAILS_BLOCKS, }; + use crate::models::sonarr_models::{DownloadRecord, DownloadStatus, Episode}; use crate::models::stateful_table::StatefulTable; + use crate::network::sonarr_network::sonarr_network_test_utils::test_utils::download_record; use crate::ui::DrawUi; - use crate::ui::sonarr_ui::library::season_details_ui::SeasonDetailsUi; + use crate::ui::sonarr_ui::library::season_details_ui::{ + SeasonDetailsUi, decorate_with_row_style, + }; + use crate::ui::styles::ManagarrStyle; use crate::ui::ui_test_utils::test_utils::render_to_string_with_app; #[test] @@ -25,6 +34,235 @@ mod tests { }); } + #[test] + fn test_decorate_with_row_style_downloaded_when_episode_has_file() { + let episode = Episode { + id: 5, + has_file: true, + monitored: true, + ..Episode::default() + }; + let row = Row::new(vec![Cell::from("test".to_owned())]); + + let style = decorate_with_row_style(&[], &episode, row.clone()); + + assert_eq!(style, row.downloaded()); + } + + #[test] + fn test_decorate_with_row_style_unmonitored_when_episode_has_file_and_is_unmonitored() { + let episode = Episode { + id: 5, + has_file: true, + monitored: false, + ..Episode::default() + }; + let row = Row::new(vec![Cell::from("test".to_owned())]); + + let style = decorate_with_row_style(&[], &episode, row.clone()); + + assert_eq!(style, row.unmonitored()); + } + + #[test] + fn test_decorate_with_row_style_missing_when_episode_has_no_file() { + let episode = Episode { + id: 5, + has_file: false, + monitored: true, + ..Episode::default() + }; + let row = Row::new(vec![Cell::from("test".to_owned())]); + + let style = decorate_with_row_style(&[], &episode, row.clone()); + + assert_eq!(style, row.missing()); + } + + #[test] + fn test_decorate_with_row_style_unmonitored_missing_when_episode_has_no_file_and_is_unmonitored() + { + let episode = Episode { + id: 5, + has_file: false, + monitored: false, + ..Episode::default() + }; + let row = Row::new(vec![Cell::from("test".to_owned())]); + + let style = decorate_with_row_style(&[], &episode, row.clone()); + + assert_eq!(style, row.unmonitored_missing()); + } + + #[test] + fn test_decorate_with_row_style_unreleased_when_episode_has_no_file_and_air_date_is_in_the_future() + { + let episode = Episode { + id: 5, + has_file: false, + monitored: true, + air_date_utc: Some(Utc::now() + Duration::days(1)), + ..Episode::default() + }; + let row = Row::new(vec![Cell::from("test".to_owned())]); + + let style = decorate_with_row_style(&[], &episode, row.clone()); + + assert_eq!(style, row.unreleased()); + } + + #[test] + fn test_decorate_with_row_style_missing_when_episode_has_no_file_and_air_date_is_in_the_past() { + let episode = Episode { + id: 5, + has_file: false, + monitored: true, + air_date_utc: Some(Utc::now() - Duration::days(1)), + ..Episode::default() + }; + let row = Row::new(vec![Cell::from("test".to_owned())]); + + let style = decorate_with_row_style(&[], &episode, row.clone()); + + assert_eq!(style, row.missing()); + } + + #[test] + fn test_decorate_with_row_style_unmonitored_missing_when_unmonitored_episode_has_no_file_and_air_date_is_in_the_future() + { + let episode = Episode { + id: 5, + has_file: false, + monitored: false, + air_date_utc: Some(Utc::now() + Duration::days(1)), + ..Episode::default() + }; + let row = Row::new(vec![Cell::from("test".to_owned())]); + + let style = decorate_with_row_style(&[], &episode, row.clone()); + + assert_eq!(style, row.unmonitored_missing()); + } + + #[test] + fn test_decorate_with_row_style_downloading_when_episode_has_no_file_and_is_downloading() { + let downloads_vec = vec![episode_download(5, DownloadStatus::Downloading)]; + let episode = Episode { + id: 5, + has_file: false, + monitored: true, + ..Episode::default() + }; + let row = Row::new(vec![Cell::from("test".to_owned())]); + + let style = decorate_with_row_style(&downloads_vec, &episode, row.clone()); + + assert_eq!(style, row.downloading()); + } + + #[test] + fn test_decorate_with_row_style_awaiting_import_when_episode_has_no_file_and_download_is_completed() + { + let downloads_vec = vec![episode_download(5, DownloadStatus::Completed)]; + let episode = Episode { + id: 5, + has_file: false, + monitored: true, + ..Episode::default() + }; + let row = Row::new(vec![Cell::from("test".to_owned())]); + + let style = decorate_with_row_style(&downloads_vec, &episode, row.clone()); + + assert_eq!(style, row.awaiting_import()); + } + + #[test] + fn test_decorate_with_row_style_missing_when_episode_download_is_neither_downloading_nor_completed() + { + let downloads_vec = vec![episode_download(5, DownloadStatus::Queued)]; + let episode = Episode { + id: 5, + has_file: false, + monitored: true, + ..Episode::default() + }; + let row = Row::new(vec![Cell::from("test".to_owned())]); + + let style = decorate_with_row_style(&downloads_vec, &episode, row.clone()); + + assert_eq!(style, row.missing()); + } + + #[test] + fn test_decorate_with_row_style_missing_when_only_a_different_episode_is_downloading() { + let downloads_vec = vec![episode_download(6, DownloadStatus::Downloading)]; + let episode = Episode { + id: 5, + has_file: false, + monitored: true, + ..Episode::default() + }; + let row = Row::new(vec![Cell::from("test".to_owned())]); + + let style = decorate_with_row_style(&downloads_vec, &episode, row.clone()); + + assert_eq!(style, row.missing()); + } + + #[test] + fn test_decorate_with_row_style_missing_when_download_has_no_episode_id() { + let downloads_vec = vec![DownloadRecord { + episode_id: None, + ..episode_download(5, DownloadStatus::Downloading) + }]; + let episode = Episode { + id: 5, + has_file: false, + monitored: true, + ..Episode::default() + }; + let row = Row::new(vec![Cell::from("test".to_owned())]); + + let style = decorate_with_row_style(&downloads_vec, &episode, row.clone()); + + assert_eq!(style, row.missing()); + } + + #[test] + fn test_decorate_with_row_style_downloading_when_unmonitored_episode_has_no_file_and_is_downloading() + { + let downloads_vec = vec![episode_download(5, DownloadStatus::Downloading)]; + let episode = Episode { + id: 5, + has_file: false, + monitored: false, + ..Episode::default() + }; + let row = Row::new(vec![Cell::from("test".to_owned())]); + + let style = decorate_with_row_style(&downloads_vec, &episode, row.clone()); + + assert_eq!(style, row.downloading()); + } + + #[test] + fn test_decorate_with_row_style_downloaded_when_episode_has_file_and_is_downloading() { + let downloads_vec = vec![episode_download(5, DownloadStatus::Downloading)]; + let episode = Episode { + id: 5, + has_file: true, + monitored: true, + ..Episode::default() + }; + let row = Row::new(vec![Cell::from("test".to_owned())]); + + let style = decorate_with_row_style(&downloads_vec, &episode, row.clone()); + + assert_eq!(style, row.downloaded()); + } + mod snapshot_tests { use crate::ui::ui_test_utils::test_utils::TerminalSize; use rstest::rstest; @@ -145,4 +383,13 @@ mod tests { insta::assert_snapshot!(output); } } + + fn episode_download(episode_id: i64, status: DownloadStatus) -> DownloadRecord { + DownloadRecord { + status, + id: 9, + episode_id: Some(Number::from(episode_id)), + ..download_record() + } + } }