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.
This commit is contained in:
Dark-Alex-17 committed 2026-09-17 15:30:16 -06:00
1 parent 289abbd8eb
commit e9c323c4e8
1 file changed
+248 -1
@@ -1,14 +1,23 @@
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use chrono::{Duration, Utc};
use pretty_assertions::assert_eq;
use ratatui::widgets::{Cell, Row};
use serde_json::Number;
use strum::IntoEnumIterator; use strum::IntoEnumIterator;
use crate::app::App; use crate::app::App;
use crate::models::servarr_data::sonarr::sonarr_data::{ use crate::models::servarr_data::sonarr::sonarr_data::{
ActiveSonarrBlock, EPISODE_DETAILS_BLOCKS, SEASON_DETAILS_BLOCKS, ActiveSonarrBlock, EPISODE_DETAILS_BLOCKS, SEASON_DETAILS_BLOCKS,
}; };
use crate::models::sonarr_models::{DownloadRecord, DownloadStatus, Episode};
use crate::models::stateful_table::StatefulTable; 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::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; use crate::ui::ui_test_utils::test_utils::render_to_string_with_app;
#[test] #[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 { mod snapshot_tests {
use crate::ui::ui_test_utils::test_utils::TerminalSize; use crate::ui::ui_test_utils::test_utils::TerminalSize;
use rstest::rstest; use rstest::rstest;
@@ -145,4 +383,13 @@ mod tests {
insta::assert_snapshot!(output); 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()
}
}
} }