Compare commits

...
6 Commits
Author SHA1 Message Date
Dark-Alex-17 bdeaf6ae35 test: prove the track, episode and movie row styles reach the rendered buffer
The row-style decorators were unit-tested as pure functions and the populated test apps do carry a downloading queue entry, but text snapshots discard colour, so nothing proved the draw functions actually call the decorator with the live downloads list. Passing an empty slice instead left every test green.

Render the lidarr tracks table, the sonarr episodes table and the radarr movies table into a Buffer and assert the probe row's foreground: downloading_style with the queue populated, missing_style once the queue is emptied. The probe is the second row so the selection highlight never masks its style.
2026-09-17 18:44:34 -06:00
Dark-Alex-17 35ed3f359d refactor: share one DownloadStatus enum across the servarr models
DownloadStatus was declared four times, byte for byte identically, in
radarr_models, sonarr_models, lidarr_models, and readarr_models: the same
variants, the same derive list, and the same serde, strum, and display_style
attributes. Every servarr's queue endpoint reports the same status
vocabulary, so there is nothing servarr specific about the type.

Move it into servarr_models, preserving that file's alphabetical ordering
(between DiskSpace and IndexerSettings), and drop the four copies. The
DownloadRecord types keep their per-servarr shapes and now reference the
shared enum.

The two enum tests, test_download_status_display and
test_download_status_to_display_str, were likewise duplicated in the four
per-servarr model test files. Keep one copy of each in servarr_models_tests
alongside the other shared enum display tests and delete the rest. The
per-servarr DownloadRecord fallback deserialization tests stay where they
are since they exercise each servarr's own record type.

Behavior is unchanged. Every remaining edit is an import path update.
2026-09-17 18:32:51 -06:00
Dark-Alex-17 040935178a test(sonarr): give the download record and episode fixtures distinct ids
The shared download record held id and episode id both equal to 1, and
the shared episode held id and episode file id both equal to 1, so
reading the wrong one of those fields could not fail any test that uses
them. Move the download record to id 9 and the episode to id 11, keeping
the download record's episode id equal to the episode's id so the
downloading episode still matches, and move the episode file to id 7
together with the episode's episode file id so the season view still
finds its quality and size. Series id, season number, and episode number
stay at 1. The handler tree keeps its own copy of these fixtures; update
it identically, along with the JSON twins in the network tests and the
seven handler assertions that named the old ids. The two get episodes
tests that asserted the sorted table now spell out the sorted order,
since the fixture episode no longer sorts first by id.
2026-09-17 18:22:31 -06:00
Dark-Alex-17 4f78a5940f test(lidarr): give the track and history fixtures distinct ids
The shared track held id, artist id, track file id, and album id all
equal to 1, and the shared history item held id, album id, artist id,
and track id all equal to 1, so reading the wrong one of those fields
could not fail any test that uses them. Move the track to id 5, artist
id 3, and track file id 7 (with the shared track file moved to id 7 so
the album view still finds its quality and audio info), and move the
history item to id 13, artist id 3, and track id 5. Both album ids stay
at 1 so the fully populated album view still shows its track as
downloading. Update the seven assertions that named the old ids.
2026-09-17 18:16:17 -06:00
Dark-Alex-17 9822597b56 fix(radarr): model the movie status as an enum
Movie.status and AddMovieSearchResult.status were bare Strings compared
against the literal "released" in the row-style decorator and in the
movie details style function. They now carry a MovieStatus enum with the
five values Radarr actually sends (tba, announced, inCinemas, released,
deleted), shaped like MinimumAvailability and DownloadStatus, and guarded
with from_json_or_default so an unrecognised status degrades to Tba
instead of failing the whole fetch.

Radarr never sends "Downloaded" as a movie status; its web UI derives
that label from released plus hasFile. Every fixture that carried
"Downloaded" now holds released, the value it was standing in for, so
none of them degrade to the default through the guard.

The status is never rendered as text in the Radarr tree, so no snapshot
changes; the CLI JSON output is unchanged since the enum serializes to
the same camelCase strings. The style_from_download_status tests now
exercise the unreleased arm, which previously had no case, in place of a
duplicated unmonitored-missing row.
2026-09-17 18:06:37 -06:00
Dark-Alex-17 c42a46c7ca fix: page up and down stop at the table edges instead of wrapping
A page is 20 rows and the index wrapped modulo one less than the row
count, so the last row was unreachable by paging, 21-row and 5-row tables
did nothing on page down from the first row, and 4-row tables cycled
0, 2, 1 and never reached row 3. Page down now clamps to the last row and
page up clamps to the first.

With no division left, a single-row table is safe, so the early return
only guards the empty table again, matching scroll_to_top and
scroll_to_bottom.
2026-09-17 17:38:37 -06:00
46 changed files with 720 additions and 464 deletions

No files matched your search

+3 -3
View File
@@ -498,7 +498,7 @@ mod tests {
assert!(app.is_loading);
assert_eq!(
rx.recv().await.unwrap(),
LidarrEvent::GetTrackDetails(1).into()
LidarrEvent::GetTrackDetails(5).into()
);
assert!(!app.data.lidarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
@@ -518,7 +518,7 @@ mod tests {
assert!(app.is_loading);
assert_eq!(
rx.recv().await.unwrap(),
LidarrEvent::GetTrackHistory(1, 1, 1).into()
LidarrEvent::GetTrackHistory(1, 1, 5).into()
);
assert!(!app.data.lidarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
@@ -759,7 +759,7 @@ mod tests {
album_details_modal.tracks.set_items(vec![track()]);
app.data.lidarr_data.album_details_modal = Some(album_details_modal);
assert_eq!(app.extract_track_id().await, 1);
assert_eq!(app.extract_track_id().await, 5);
}
#[tokio::test]
+1 -1
View File
@@ -631,7 +631,7 @@ mod tests {
async fn test_handle_list_track_history_command() {
let expected_artist_id = 1;
let expected_album_id = 1;
let expected_track_id = 1;
let expected_track_id = 5;
let mut mock_network = MockNetworkTrait::new();
mock_network
.expect_handle_network_event()
+9
View File
@@ -171,10 +171,19 @@ mod property_tests {
table.set_items(movies);
for i in 0..page_ops {
let previous_index = table.state.selected().unwrap();
if i % 2 == 0 {
table.page_down();
prop_assert_eq!(
table.state.selected(),
Some((previous_index + 20).min(list_size - 1))
);
} else {
table.page_up();
prop_assert_eq!(table.state.selected(), Some(previous_index.saturating_sub(20)));
}
let current = table.current_selection();
@@ -255,7 +255,7 @@ mod tests {
)]
#[case(
ActiveLidarrBlock::DeleteTrackFilePrompt,
LidarrEvent::DeleteTrackFile(1)
LidarrEvent::DeleteTrackFile(7)
)]
fn test_album_details_prompt_confirm_submit(
#[case] prompt_block: ActiveLidarrBlock,
@@ -606,7 +606,7 @@ mod tests {
)]
#[case(
ActiveLidarrBlock::DeleteTrackFilePrompt,
LidarrEvent::DeleteTrackFile(1)
LidarrEvent::DeleteTrackFile(7)
)]
fn test_album_details_prompt_confirm_confirm_key(
#[case] prompt_block: ActiveLidarrBlock,
@@ -752,7 +752,7 @@ mod tests {
)
.extract_track_file_id();
assert_eq!(track_file_id, 1);
assert_eq!(track_file_id, 7);
}
#[test]
@@ -4,11 +4,11 @@ pub(in crate::handlers::radarr_handlers) mod utils {
use crate::models::HorizontallyScrollableText;
use crate::models::radarr_models::{
AddMovieBody, AddMovieOptions, AddMovieSearchResult, Collection, CollectionMovie,
DownloadRecord, DownloadStatus, IndexerSettings, MediaInfo, MinimumAvailability, Movie,
MovieCollection, MovieFile, MovieMonitor, RadarrRelease, Rating, RatingsList,
DownloadRecord, IndexerSettings, MediaInfo, MinimumAvailability, Movie, MovieCollection,
MovieFile, MovieMonitor, MovieStatus, RadarrRelease, Rating, RatingsList,
};
use crate::models::servarr_models::{
Indexer, IndexerField, Language, Quality, QualityWrapper, RootFolder,
DownloadStatus, Indexer, IndexerField, Language, Quality, QualityWrapper, RootFolder,
};
use chrono::DateTime;
use serde_json::{Number, json};
@@ -329,7 +329,7 @@ pub(in crate::handlers::radarr_handlers) mod utils {
title: "Test".to_owned().into(),
original_language: language(),
size_on_disk: 3543348019,
status: "Downloaded".to_owned(),
status: MovieStatus::Released,
overview: "Blah blah blah".to_owned(),
path: "/nfs/movies".to_owned(),
studio: Some("21st Century Alex".to_owned()),
@@ -395,7 +395,7 @@ pub(in crate::handlers::radarr_handlers) mod utils {
tmdb_id: 4321,
title: HorizontallyScrollableText::from("Test Search Result"),
original_language: language(),
status: "released".to_owned(),
status: MovieStatus::Released,
overview: "New movie blah blah blah".to_owned(),
genres: genres(),
year: 2023,
@@ -11,8 +11,9 @@ mod tests {
use crate::event::Key;
use crate::handlers::KeyEventHandler;
use crate::handlers::readarr_handlers::downloads::DownloadsHandler;
use crate::models::readarr_models::{DownloadRecord, DownloadStatus};
use crate::models::readarr_models::DownloadRecord;
use crate::models::servarr_data::readarr::readarr_data::{ActiveReadarrBlock, DOWNLOADS_BLOCKS};
use crate::models::servarr_models::DownloadStatus;
mod test_handle_delete {
use pretty_assertions::assert_eq;
@@ -140,7 +140,7 @@ mod tests {
#[case(
ActiveSonarrBlock::Downloads,
ActiveSonarrBlock::DeleteDownloadPrompt,
SonarrEvent::DeleteDownload(1)
SonarrEvent::DeleteDownload(9)
)]
#[case(
ActiveSonarrBlock::Downloads,
@@ -340,7 +340,7 @@ mod tests {
#[case(
ActiveSonarrBlock::Downloads,
ActiveSonarrBlock::DeleteDownloadPrompt,
SonarrEvent::DeleteDownload(1)
SonarrEvent::DeleteDownload(9)
)]
#[case(
ActiveSonarrBlock::Downloads,
@@ -431,7 +431,7 @@ mod tests {
)
.extract_download_id();
assert_eq!(download_id, 1);
assert_eq!(download_id, 9);
}
#[test]
@@ -207,7 +207,7 @@ mod tests {
#[rstest]
#[case(
ActiveSonarrBlock::AutomaticallySearchEpisodePrompt,
SonarrEvent::TriggerAutomaticEpisodeSearch(1)
SonarrEvent::TriggerAutomaticEpisodeSearch(11)
)]
fn test_episode_details_prompt_confirm_submit(
#[case] prompt_block: ActiveSonarrBlock,
@@ -646,7 +646,7 @@ mod tests {
assert_navigation_popped!(app, active_sonarr_block.into());
assert_some_eq_x!(
&app.data.sonarr_data.prompt_confirm_action,
&SonarrEvent::TriggerAutomaticEpisodeSearch(1)
&SonarrEvent::TriggerAutomaticEpisodeSearch(11)
);
}
@@ -787,7 +787,7 @@ mod tests {
)
.extract_episode_id();
assert_eq!(episode_id, 1);
assert_eq!(episode_id, 11);
}
#[test]
@@ -673,7 +673,7 @@ mod tests {
assert!(app.is_routing);
assert_some_eq_x!(
&app.data.sonarr_data.prompt_confirm_action,
&SonarrEvent::ToggleEpisodeMonitoring(1)
&SonarrEvent::ToggleEpisodeMonitoring(11)
);
}
@@ -1129,7 +1129,7 @@ mod tests {
)
.extract_episode_id();
assert_eq!(episode_id, 1);
assert_eq!(episode_id, 11);
}
#[test]
@@ -4,12 +4,12 @@ pub(in crate::handlers::sonarr_handlers) mod utils {
use crate::models::HorizontallyScrollableText;
use crate::models::servarr_models::IndexerSettings;
use crate::models::servarr_models::{
Indexer, IndexerField, Language, Quality, QualityWrapper, RootFolder,
DownloadStatus, Indexer, IndexerField, Language, Quality, QualityWrapper, RootFolder,
};
use crate::models::sonarr_models::{
AddSeriesSearchResult, AddSeriesSearchResultStatistics, DownloadRecord, DownloadStatus,
Episode, EpisodeFile, MediaInfo, Rating, Season, SeasonStatistics, Series, SeriesStatistics,
SeriesStatus, SeriesType,
AddSeriesSearchResult, AddSeriesSearchResultStatistics, DownloadRecord, Episode, EpisodeFile,
MediaInfo, Rating, Season, SeasonStatistics, Series, SeriesStatistics, SeriesStatus,
SeriesType,
};
use chrono::DateTime;
use serde_json::{Number, json};
@@ -191,8 +191,8 @@ pub(in crate::handlers::sonarr_handlers) mod utils {
DownloadRecord {
title: "Test Download Title".to_owned(),
status: DownloadStatus::Downloading,
id: 1,
episode_id: Some(Number::from(1i64)),
id: 9,
episode_id: Some(Number::from(11i64)),
size: 3543348019f64,
sizeleft: 1771674009f64,
output_path: Some(HorizontallyScrollableText::from(
@@ -205,10 +205,10 @@ pub(in crate::handlers::sonarr_handlers) mod utils {
pub fn episode() -> Episode {
Episode {
id: 1,
id: 11,
series_id: 1,
tvdb_id: 1234,
episode_file_id: 1,
episode_file_id: 7,
season_number: 1,
episode_number: 1,
title: "Something cool".to_owned(),
@@ -224,7 +224,7 @@ pub(in crate::handlers::sonarr_handlers) mod utils {
pub fn episode_file() -> EpisodeFile {
EpisodeFile {
id: 1,
id: 7,
relative_path: "/season 1/episode 1.mkv".to_owned(),
path: "/nfs/tv/series/season 1/episode 1.mkv".to_owned(),
size: 3543348019,
+3 -2
View File
@@ -606,17 +606,18 @@ mod tests {
.take(100)
.collect();
app.data.radarr_data.movies.set_items(movies_vec);
app.data.radarr_data.movies.state.select(Some(50));
TableHandlerUnit::new(key, &mut app, ActiveRadarrBlock::Movies, None).handle();
if key == Key::PgUp {
assert_str_eq!(
app.data.radarr_data.movies.current_selection().title.text,
"Test 79"
"Test 30"
);
} else {
assert_str_eq!(
app.data.radarr_data.movies.current_selection().title.text,
"Test 20"
"Test 70"
);
}
}
+2 -32
View File
@@ -1,8 +1,8 @@
use super::{
HorizontallyScrollableText, Serdeable,
servarr_models::{
DiskSpace, HostConfig, Indexer, IndexerTestResult, MetadataProfile, QualityProfile,
QualityWrapper, RootFolder, SecurityConfig, SystemStatus, Tag,
DiskSpace, DownloadStatus, HostConfig, Indexer, IndexerTestResult, MetadataProfile,
QualityProfile, QualityWrapper, RootFolder, SecurityConfig, SystemStatus, Tag,
},
};
use crate::models::servarr_models::{IndexerSettings, LogResponse, QueueEvent, Update};
@@ -181,36 +181,6 @@ pub struct DownloadRecord {
impl Eq for DownloadRecord {}
#[derive(
Serialize,
Deserialize,
Default,
PartialEq,
Eq,
Clone,
Copy,
Debug,
EnumIter,
Display,
EnumDisplayStyle,
)]
#[serde(rename_all = "camelCase")]
#[strum(serialize_all = "camelCase")]
pub enum DownloadStatus {
#[default]
Unknown,
Queued,
Paused,
Downloading,
Completed,
Failed,
Warning,
Delay,
#[display_style(name = "Download Client Unavailable")]
DownloadClientUnavailable,
Fallback,
}
#[derive(Default, Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct DownloadsResponse {
+6 -40
View File
@@ -7,14 +7,14 @@ mod tests {
use crate::models::lidarr_models::{
AddArtistSearchResult, Album, AudioTags, BlocklistItem, BlocklistResponse, DownloadRecord,
DownloadStatus, DownloadsResponse, LidarrHistoryEventType, LidarrHistoryItem,
LidarrHistoryWrapper, LidarrRelease, LidarrTask, LidarrTaskName, MediaInfo, Member,
MonitorType, NewItemMonitorType, Track, TrackFile,
DownloadsResponse, LidarrHistoryEventType, LidarrHistoryItem, LidarrHistoryWrapper,
LidarrRelease, LidarrTask, LidarrTaskName, MediaInfo, Member, MonitorType, NewItemMonitorType,
Track, TrackFile,
};
use crate::models::servarr_models::{
DiskSpace, HostConfig, Indexer, IndexerSettings, IndexerTestResult, Log, LogResponse,
MetadataProfile, QualityProfile, QueueEvent, RootFolder, SecurityConfig, SystemStatus, Tag,
Update,
DiskSpace, DownloadStatus, HostConfig, Indexer, IndexerSettings, IndexerTestResult, Log,
LogResponse, MetadataProfile, QualityProfile, QueueEvent, RootFolder, SecurityConfig,
SystemStatus, Tag, Update,
};
use crate::models::{
Serdeable,
@@ -659,40 +659,6 @@ mod tests {
assert_str_eq!(ArtistStatus::Deleted.to_display_str(), "Deleted");
}
#[test]
fn test_download_status_display() {
assert_str_eq!(DownloadStatus::Unknown.to_string(), "unknown");
assert_str_eq!(DownloadStatus::Queued.to_string(), "queued");
assert_str_eq!(DownloadStatus::Paused.to_string(), "paused");
assert_str_eq!(DownloadStatus::Downloading.to_string(), "downloading");
assert_str_eq!(DownloadStatus::Completed.to_string(), "completed");
assert_str_eq!(DownloadStatus::Failed.to_string(), "failed");
assert_str_eq!(DownloadStatus::Warning.to_string(), "warning");
assert_str_eq!(DownloadStatus::Delay.to_string(), "delay");
assert_str_eq!(
DownloadStatus::DownloadClientUnavailable.to_string(),
"downloadClientUnavailable"
);
assert_str_eq!(DownloadStatus::Fallback.to_string(), "fallback");
}
#[test]
fn test_download_status_to_display_str() {
assert_str_eq!(DownloadStatus::Unknown.to_display_str(), "Unknown");
assert_str_eq!(DownloadStatus::Queued.to_display_str(), "Queued");
assert_str_eq!(DownloadStatus::Paused.to_display_str(), "Paused");
assert_str_eq!(DownloadStatus::Downloading.to_display_str(), "Downloading");
assert_str_eq!(DownloadStatus::Completed.to_display_str(), "Completed");
assert_str_eq!(DownloadStatus::Failed.to_display_str(), "Failed");
assert_str_eq!(DownloadStatus::Warning.to_display_str(), "Warning");
assert_str_eq!(DownloadStatus::Delay.to_display_str(), "Delay");
assert_str_eq!(
DownloadStatus::DownloadClientUnavailable.to_display_str(),
"Download Client Unavailable"
);
assert_str_eq!(DownloadStatus::Fallback.to_display_str(), "Fallback");
}
#[test]
fn test_lidarr_history_event_type_display() {
assert_str_eq!(LidarrHistoryEventType::Unknown.to_string(), "unknown");
+32 -34
View File
@@ -12,8 +12,8 @@ use strum_macros::{Display, EnumIter};
use super::Serdeable;
use super::servarr_models::{
DiskSpace, HostConfig, Indexer, Language, LogResponse, QualityProfile, QualityWrapper,
QueueEvent, RootFolder, SecurityConfig, SystemStatus, Tag, Update,
DiskSpace, DownloadStatus, HostConfig, Indexer, Language, LogResponse, QualityProfile,
QualityWrapper, QueueEvent, RootFolder, SecurityConfig, SystemStatus, Tag, Update,
};
#[cfg(test)]
@@ -42,7 +42,8 @@ pub struct AddMovieSearchResult {
pub tmdb_id: i64,
pub title: HorizontallyScrollableText,
pub original_language: Language,
pub status: String,
#[serde(deserialize_with = "super::from_json_or_default")]
pub status: MovieStatus,
pub overview: String,
pub genres: Vec<String>,
#[serde(deserialize_with = "super::from_i64")]
@@ -167,36 +168,6 @@ pub struct DownloadRecord {
pub download_client: String,
}
#[derive(
Serialize,
Deserialize,
Default,
PartialEq,
Eq,
Clone,
Copy,
Debug,
EnumIter,
Display,
EnumDisplayStyle,
)]
#[serde(rename_all = "camelCase")]
#[strum(serialize_all = "camelCase")]
pub enum DownloadStatus {
#[default]
Unknown,
Queued,
Paused,
Downloading,
Completed,
Failed,
Warning,
Delay,
#[display_style(name = "Download Client Unavailable")]
DownloadClientUnavailable,
Fallback,
}
#[derive(Default, Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct DownloadsResponse {
@@ -333,7 +304,8 @@ pub struct Movie {
pub original_language: Language,
#[serde(deserialize_with = "super::from_i64")]
pub size_on_disk: i64,
pub status: String,
#[serde(deserialize_with = "super::from_json_or_default")]
pub status: MovieStatus,
pub overview: String,
pub path: String,
pub studio: Option<String>,
@@ -357,6 +329,32 @@ pub struct Movie {
pub collection: Option<MovieCollection>,
}
#[derive(
Serialize,
Deserialize,
Default,
PartialEq,
Eq,
Clone,
Copy,
Debug,
EnumIter,
Display,
EnumDisplayStyle,
)]
#[serde(rename_all = "camelCase")]
#[strum(serialize_all = "camelCase")]
pub enum MovieStatus {
#[default]
#[display_style(name = "TBA")]
Tba,
Announced,
#[display_style(name = "In Cinemas")]
InCinemas,
Released,
Deleted,
}
#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct MovieCollection {
+67 -39
View File
@@ -11,16 +11,17 @@ mod tests {
Serdeable,
radarr_models::{
AddMovieSearchResult, BlocklistItem, BlocklistResponse, Collection, Credit, CreditType,
DiskSpace, DownloadRecord, DownloadStatus, DownloadsResponse, Indexer, IndexerSettings,
IndexerTestResult, MinimumAvailability, Movie, MovieHistoryItem, MovieMonitor,
QualityProfile, RadarrRelease, RadarrSerdeable, RadarrTask, RadarrTaskName, Tag, Update,
DiskSpace, DownloadRecord, DownloadsResponse, Indexer, IndexerSettings, IndexerTestResult,
MinimumAvailability, Movie, MovieHistoryItem, MovieMonitor, MovieStatus, QualityProfile,
RadarrRelease, RadarrSerdeable, RadarrTask, RadarrTaskName, Tag, Update,
},
servarr_models::{
HostConfig, Log, LogResponse, QueueEvent, RootFolder, SecurityConfig, SystemStatus,
DownloadStatus, HostConfig, Log, LogResponse, QueueEvent, RootFolder, SecurityConfig,
SystemStatus,
},
};
use crate::network::radarr_network::radarr_network_test_utils::test_utils::{
MOVIE_JSON, collection, crew_credit, download_record, task,
MOVIE_JSON, add_movie_search_result, collection, crew_credit, download_record, task,
};
#[test]
@@ -50,6 +51,24 @@ mod tests {
assert_str_eq!(MinimumAvailability::Released.to_display_str(), "Released");
}
#[test]
fn test_movie_status_display() {
assert_str_eq!(MovieStatus::Tba.to_string(), "tba");
assert_str_eq!(MovieStatus::Announced.to_string(), "announced");
assert_str_eq!(MovieStatus::InCinemas.to_string(), "inCinemas");
assert_str_eq!(MovieStatus::Released.to_string(), "released");
assert_str_eq!(MovieStatus::Deleted.to_string(), "deleted");
}
#[test]
fn test_movie_status_to_display_str() {
assert_str_eq!(MovieStatus::Tba.to_display_str(), "TBA");
assert_str_eq!(MovieStatus::Announced.to_display_str(), "Announced");
assert_str_eq!(MovieStatus::InCinemas.to_display_str(), "In Cinemas");
assert_str_eq!(MovieStatus::Released.to_display_str(), "Released");
assert_str_eq!(MovieStatus::Deleted.to_display_str(), "Deleted");
}
#[test]
fn test_monitor_display() {
assert_str_eq!(MovieMonitor::MovieOnly.to_string(), "movieOnly");
@@ -70,40 +89,6 @@ mod tests {
assert_str_eq!(MovieMonitor::None.to_display_str(), "None");
}
#[test]
fn test_download_status_display() {
assert_str_eq!(DownloadStatus::Unknown.to_string(), "unknown");
assert_str_eq!(DownloadStatus::Queued.to_string(), "queued");
assert_str_eq!(DownloadStatus::Paused.to_string(), "paused");
assert_str_eq!(DownloadStatus::Downloading.to_string(), "downloading");
assert_str_eq!(DownloadStatus::Completed.to_string(), "completed");
assert_str_eq!(DownloadStatus::Failed.to_string(), "failed");
assert_str_eq!(DownloadStatus::Warning.to_string(), "warning");
assert_str_eq!(DownloadStatus::Delay.to_string(), "delay");
assert_str_eq!(
DownloadStatus::DownloadClientUnavailable.to_string(),
"downloadClientUnavailable"
);
assert_str_eq!(DownloadStatus::Fallback.to_string(), "fallback");
}
#[test]
fn test_download_status_to_display_str() {
assert_str_eq!(DownloadStatus::Unknown.to_display_str(), "Unknown");
assert_str_eq!(DownloadStatus::Queued.to_display_str(), "Queued");
assert_str_eq!(DownloadStatus::Paused.to_display_str(), "Paused");
assert_str_eq!(DownloadStatus::Downloading.to_display_str(), "Downloading");
assert_str_eq!(DownloadStatus::Completed.to_display_str(), "Completed");
assert_str_eq!(DownloadStatus::Failed.to_display_str(), "Failed");
assert_str_eq!(DownloadStatus::Warning.to_display_str(), "Warning");
assert_str_eq!(DownloadStatus::Delay.to_display_str(), "Delay");
assert_str_eq!(
DownloadStatus::DownloadClientUnavailable.to_display_str(),
"Download Client Unavailable"
);
assert_str_eq!(DownloadStatus::Fallback.to_display_str(), "Fallback");
}
#[test]
fn test_radarr_history_event_type_display() {
assert_str_eq!(RadarrHistoryEventType::Unknown.to_string(), "unknown");
@@ -681,6 +666,49 @@ mod tests {
);
}
#[test]
fn test_movie_status_deserialization_falls_back_to_default() {
let expected = Movie {
status: MovieStatus::default(),
..serde_json::from_str(MOVIE_JSON).unwrap()
};
assert_eq!(
deserialize_with_field::<Movie>(MOVIE_JSON, "status", json!(2)),
expected
);
assert_eq!(
deserialize_with_field::<Movie>(MOVIE_JSON, "status", json!("notAThing")),
expected
);
}
#[test]
fn test_add_movie_search_result_status_deserialization_falls_back_to_default() {
let add_movie_search_result_json = serde_json::to_string(&add_movie_search_result()).unwrap();
let expected = AddMovieSearchResult {
status: MovieStatus::default(),
..add_movie_search_result()
};
assert_eq!(
deserialize_with_field::<AddMovieSearchResult>(
&add_movie_search_result_json,
"status",
json!(2)
),
expected
);
assert_eq!(
deserialize_with_field::<AddMovieSearchResult>(
&add_movie_search_result_json,
"status",
json!("notAThing")
),
expected
);
}
#[test]
fn test_collection_minimum_availability_deserialization_falls_back_to_default() {
let collection_json = serde_json::to_string(&collection()).unwrap();
+2 -32
View File
@@ -1,8 +1,8 @@
use super::{
HorizontallyScrollableText, Serdeable,
servarr_models::{
DiskSpace, HostConfig, Indexer, IndexerTestResult, MetadataProfile, QualityProfile,
QualityWrapper, RootFolder, SecurityConfig, SystemStatus, Tag,
DiskSpace, DownloadStatus, HostConfig, Indexer, IndexerTestResult, MetadataProfile,
QualityProfile, QualityWrapper, RootFolder, SecurityConfig, SystemStatus, Tag,
},
};
use crate::models::servarr_models::{IndexerSettings, LogResponse, QueueEvent, Update};
@@ -172,36 +172,6 @@ pub struct DownloadRecord {
impl Eq for DownloadRecord {}
#[derive(
Serialize,
Deserialize,
Default,
PartialEq,
Eq,
Clone,
Copy,
Debug,
EnumIter,
Display,
EnumDisplayStyle,
)]
#[serde(rename_all = "camelCase")]
#[strum(serialize_all = "camelCase")]
pub enum DownloadStatus {
#[default]
Unknown,
Queued,
Paused,
Downloading,
Completed,
Failed,
Warning,
Delay,
#[display_style(name = "Download Client Unavailable")]
DownloadClientUnavailable,
Fallback,
}
#[derive(Default, Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct DownloadsResponse {
+4 -38
View File
@@ -7,14 +7,14 @@ mod tests {
use crate::models::readarr_models::{
AddAuthorSearchResult, BlocklistItem, BlocklistResponse, Book, BookFile, DownloadRecord,
DownloadStatus, DownloadsResponse, Edition, MediaInfo, MonitorType, NewItemMonitorType,
DownloadsResponse, Edition, MediaInfo, MonitorType, NewItemMonitorType,
ReadarrHistoryEventType, ReadarrHistoryItem, ReadarrHistoryWrapper, ReadarrRelease,
ReadarrTask, ReadarrTaskName,
};
use crate::models::servarr_models::{
DiskSpace, HostConfig, Indexer, IndexerSettings, IndexerTestResult, Log, LogResponse,
MetadataProfile, QualityProfile, QueueEvent, RootFolder, SecurityConfig, SystemStatus, Tag,
Update,
DiskSpace, DownloadStatus, HostConfig, Indexer, IndexerSettings, IndexerTestResult, Log,
LogResponse, MetadataProfile, QualityProfile, QueueEvent, RootFolder, SecurityConfig,
SystemStatus, Tag, Update,
};
use crate::models::{
Serdeable,
@@ -644,40 +644,6 @@ mod tests {
assert_str_eq!(AuthorStatus::Deleted.to_display_str(), "Deleted");
}
#[test]
fn test_download_status_display() {
assert_str_eq!(DownloadStatus::Unknown.to_string(), "unknown");
assert_str_eq!(DownloadStatus::Queued.to_string(), "queued");
assert_str_eq!(DownloadStatus::Paused.to_string(), "paused");
assert_str_eq!(DownloadStatus::Downloading.to_string(), "downloading");
assert_str_eq!(DownloadStatus::Completed.to_string(), "completed");
assert_str_eq!(DownloadStatus::Failed.to_string(), "failed");
assert_str_eq!(DownloadStatus::Warning.to_string(), "warning");
assert_str_eq!(DownloadStatus::Delay.to_string(), "delay");
assert_str_eq!(
DownloadStatus::DownloadClientUnavailable.to_string(),
"downloadClientUnavailable"
);
assert_str_eq!(DownloadStatus::Fallback.to_string(), "fallback");
}
#[test]
fn test_download_status_to_display_str() {
assert_str_eq!(DownloadStatus::Unknown.to_display_str(), "Unknown");
assert_str_eq!(DownloadStatus::Queued.to_display_str(), "Queued");
assert_str_eq!(DownloadStatus::Paused.to_display_str(), "Paused");
assert_str_eq!(DownloadStatus::Downloading.to_display_str(), "Downloading");
assert_str_eq!(DownloadStatus::Completed.to_display_str(), "Completed");
assert_str_eq!(DownloadStatus::Failed.to_display_str(), "Failed");
assert_str_eq!(DownloadStatus::Warning.to_display_str(), "Warning");
assert_str_eq!(DownloadStatus::Delay.to_display_str(), "Delay");
assert_str_eq!(
DownloadStatus::DownloadClientUnavailable.to_display_str(),
"Download Client Unavailable"
);
assert_str_eq!(DownloadStatus::Fallback.to_display_str(), "Fallback");
}
#[test]
fn test_readarr_history_event_type_display() {
assert_str_eq!(ReadarrHistoryEventType::Unknown.to_string(), "unknown");
@@ -31,10 +31,10 @@ use strum::EnumIter;
use {
super::modals::EditionDetailsModal,
crate::models::readarr_models::{
AuthorStatus, BookFile, BookStatistics, DownloadStatus, Edition, MediaInfo, MonitorType,
NewItemMonitorType, Ratings, ReadarrHistoryEventType, ReadarrTaskName,
AuthorStatus, BookFile, BookStatistics, Edition, MediaInfo, MonitorType, NewItemMonitorType,
Ratings, ReadarrHistoryEventType, ReadarrTaskName,
},
crate::models::servarr_models::{IndexerField, Quality, QualityWrapper},
crate::models::servarr_models::{DownloadStatus, IndexerField, Quality, QualityWrapper},
crate::models::stateful_table::SortOption,
crate::network::servarr_test_utils::{
diskspace, indexer_settings, indexer_test_result, queued_event,
+32
View File
@@ -2,8 +2,10 @@ use std::fmt::{Display, Formatter, Result};
use chrono::{DateTime, Utc};
use clap::ValueEnum;
use enum_display_style_derive::EnumDisplayStyle;
use serde::{Deserialize, Serialize};
use serde_json::{Number, Value};
use strum::{Display, EnumIter};
use super::HorizontallyScrollableText;
@@ -90,6 +92,36 @@ pub struct DiskSpace {
pub total_space: i64,
}
#[derive(
Serialize,
Deserialize,
Default,
PartialEq,
Eq,
Clone,
Copy,
Debug,
EnumIter,
Display,
EnumDisplayStyle,
)]
#[serde(rename_all = "camelCase")]
#[strum(serialize_all = "camelCase")]
pub enum DownloadStatus {
#[default]
Unknown,
Queued,
Paused,
Downloading,
Completed,
Failed,
Warning,
Delay,
#[display_style(name = "Download Client Unavailable")]
DownloadClientUnavailable,
Fallback,
}
#[derive(Default, Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct IndexerSettings {
+36 -2
View File
@@ -5,8 +5,8 @@ mod tests {
use serde_json::{Value, json};
use crate::models::servarr_models::{
AuthenticationMethod, AuthenticationRequired, CertificateValidation, Indexer, QualityProfile,
SecurityConfig, Update, UpdateChanges,
AuthenticationMethod, AuthenticationRequired, CertificateValidation, DownloadStatus, Indexer,
QualityProfile, SecurityConfig, Update, UpdateChanges,
};
#[test]
@@ -56,6 +56,40 @@ mod tests {
assert_str_eq!(CertificateValidation::Disabled.to_string(), "disabled");
}
#[test]
fn test_download_status_display() {
assert_str_eq!(DownloadStatus::Unknown.to_string(), "unknown");
assert_str_eq!(DownloadStatus::Queued.to_string(), "queued");
assert_str_eq!(DownloadStatus::Paused.to_string(), "paused");
assert_str_eq!(DownloadStatus::Downloading.to_string(), "downloading");
assert_str_eq!(DownloadStatus::Completed.to_string(), "completed");
assert_str_eq!(DownloadStatus::Failed.to_string(), "failed");
assert_str_eq!(DownloadStatus::Warning.to_string(), "warning");
assert_str_eq!(DownloadStatus::Delay.to_string(), "delay");
assert_str_eq!(
DownloadStatus::DownloadClientUnavailable.to_string(),
"downloadClientUnavailable"
);
assert_str_eq!(DownloadStatus::Fallback.to_string(), "fallback");
}
#[test]
fn test_download_status_to_display_str() {
assert_str_eq!(DownloadStatus::Unknown.to_display_str(), "Unknown");
assert_str_eq!(DownloadStatus::Queued.to_display_str(), "Queued");
assert_str_eq!(DownloadStatus::Paused.to_display_str(), "Paused");
assert_str_eq!(DownloadStatus::Downloading.to_display_str(), "Downloading");
assert_str_eq!(DownloadStatus::Completed.to_display_str(), "Completed");
assert_str_eq!(DownloadStatus::Failed.to_display_str(), "Failed");
assert_str_eq!(DownloadStatus::Warning.to_display_str(), "Warning");
assert_str_eq!(DownloadStatus::Delay.to_display_str(), "Delay");
assert_str_eq!(
DownloadStatus::DownloadClientUnavailable.to_display_str(),
"Download Client Unavailable"
);
assert_str_eq!(DownloadStatus::Fallback.to_display_str(), "Fallback");
}
#[test]
fn test_quality_profile_from_tuple_ref() {
let id = 2;
+2 -32
View File
@@ -16,8 +16,8 @@ use strum_macros::Display;
use super::{
HorizontallyScrollableText, Serdeable,
servarr_models::{
DiskSpace, HostConfig, Indexer, Language, LogResponse, QualityProfile, QualityWrapper,
QueueEvent, RootFolder, SecurityConfig, SystemStatus, Tag, Update,
DiskSpace, DownloadStatus, HostConfig, Indexer, Language, LogResponse, QualityProfile,
QualityWrapper, QueueEvent, RootFolder, SecurityConfig, SystemStatus, Tag, Update,
},
};
@@ -129,36 +129,6 @@ pub struct DownloadRecord {
impl Eq for DownloadRecord {}
#[derive(
Serialize,
Deserialize,
Default,
PartialEq,
Eq,
Clone,
Copy,
Debug,
EnumIter,
Display,
EnumDisplayStyle,
)]
#[serde(rename_all = "camelCase")]
#[strum(serialize_all = "camelCase")]
pub enum DownloadStatus {
#[default]
Unknown,
Queued,
Paused,
Downloading,
Completed,
Failed,
Warning,
Delay,
#[display_style(name = "Download Client Unavailable")]
DownloadClientUnavailable,
Fallback,
}
#[derive(Default, Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct DownloadsResponse {
+4 -38
View File
@@ -7,13 +7,13 @@ mod tests {
use crate::models::{
Serdeable,
servarr_models::{
DiskSpace, HostConfig, Indexer, IndexerSettings, IndexerTestResult, Language, Log,
LogResponse, QualityProfile, QueueEvent, RootFolder, SecurityConfig, SystemStatus, Tag,
DiskSpace, DownloadStatus, HostConfig, Indexer, IndexerSettings, IndexerTestResult, Language,
Log, LogResponse, QualityProfile, QueueEvent, RootFolder, SecurityConfig, SystemStatus, Tag,
Update,
},
sonarr_models::{
AddSeriesSearchResult, BlocklistItem, BlocklistResponse, DownloadRecord, DownloadStatus,
DownloadsResponse, Episode, EpisodeFile, Series, SeriesMonitor, SeriesStatus, SeriesType,
AddSeriesSearchResult, BlocklistItem, BlocklistResponse, DownloadRecord, DownloadsResponse,
Episode, EpisodeFile, Series, SeriesMonitor, SeriesStatus, SeriesType,
SonarrHistoryEventType, SonarrHistoryItem, SonarrRelease, SonarrSerdeable, SonarrTask,
SonarrTaskName,
},
@@ -123,40 +123,6 @@ mod tests {
assert_str_eq!(SeriesType::Anime.to_display_str(), "Anime");
}
#[test]
fn test_download_status_display() {
assert_str_eq!(DownloadStatus::Unknown.to_string(), "unknown");
assert_str_eq!(DownloadStatus::Queued.to_string(), "queued");
assert_str_eq!(DownloadStatus::Paused.to_string(), "paused");
assert_str_eq!(DownloadStatus::Downloading.to_string(), "downloading");
assert_str_eq!(DownloadStatus::Completed.to_string(), "completed");
assert_str_eq!(DownloadStatus::Failed.to_string(), "failed");
assert_str_eq!(DownloadStatus::Warning.to_string(), "warning");
assert_str_eq!(DownloadStatus::Delay.to_string(), "delay");
assert_str_eq!(
DownloadStatus::DownloadClientUnavailable.to_string(),
"downloadClientUnavailable"
);
assert_str_eq!(DownloadStatus::Fallback.to_string(), "fallback");
}
#[test]
fn test_download_status_to_display_str() {
assert_str_eq!(DownloadStatus::Unknown.to_display_str(), "Unknown");
assert_str_eq!(DownloadStatus::Queued.to_display_str(), "Queued");
assert_str_eq!(DownloadStatus::Paused.to_display_str(), "Paused");
assert_str_eq!(DownloadStatus::Downloading.to_display_str(), "Downloading");
assert_str_eq!(DownloadStatus::Completed.to_display_str(), "Completed");
assert_str_eq!(DownloadStatus::Failed.to_display_str(), "Failed");
assert_str_eq!(DownloadStatus::Warning.to_display_str(), "Warning");
assert_str_eq!(DownloadStatus::Delay.to_display_str(), "Delay");
assert_str_eq!(
DownloadStatus::DownloadClientUnavailable.to_display_str(),
"Download Client Unavailable"
);
assert_str_eq!(DownloadStatus::Fallback.to_display_str(), "Fallback");
}
#[test]
fn test_sonarr_history_event_type_display() {
assert_str_eq!(SonarrHistoryEventType::Unknown.to_string(), "unknown",);
+4 -7
View File
@@ -100,29 +100,26 @@ where
{
fn page_down(&mut self) {
let items_len = self.active_items().len();
if items_len <= 1 {
if items_len == 0 {
return;
}
let state = self.active_state_mut();
match state.selected() {
Some(i) => state.select(Some(i.saturating_add(20) % (items_len - 1))),
Some(i) => state.select(Some(i.saturating_add(20).min(items_len - 1))),
None => state.select_first(),
}
}
fn page_up(&mut self) {
let items_len = self.active_items().len();
if items_len <= 1 {
if items_len == 0 {
return;
}
let state = self.active_state_mut();
match state.selected() {
Some(i) => {
let len = items_len - 1;
state.select(Some((i + len - (20 % len)) % len));
}
Some(i) => state.select(Some(i.saturating_sub(20))),
None => state.select_last(),
}
}
+150 -38
View File
@@ -318,6 +318,69 @@ mod tests {
20
);
stateful_table.page_down();
assert_some_eq_x!(
stateful_table.filtered_state.as_ref().unwrap().selected(),
40
);
stateful_table.page_down();
assert_some_eq_x!(
stateful_table.filtered_state.as_ref().unwrap().selected(),
60
);
stateful_table.page_down();
assert_some_eq_x!(
stateful_table.filtered_state.as_ref().unwrap().selected(),
80
);
stateful_table.page_down();
assert_some_eq_x!(
stateful_table.filtered_state.as_ref().unwrap().selected(),
99
);
stateful_table.page_down();
assert_some_eq_x!(
stateful_table.filtered_state.as_ref().unwrap().selected(),
99
);
stateful_table.page_up();
assert_some_eq_x!(
stateful_table.filtered_state.as_ref().unwrap().selected(),
79
);
stateful_table.page_up();
assert_some_eq_x!(
stateful_table.filtered_state.as_ref().unwrap().selected(),
59
);
stateful_table.page_up();
assert_some_eq_x!(
stateful_table.filtered_state.as_ref().unwrap().selected(),
39
);
stateful_table.page_up();
assert_some_eq_x!(
stateful_table.filtered_state.as_ref().unwrap().selected(),
19
);
stateful_table.page_up();
assert_some_eq_x!(
@@ -327,33 +390,10 @@ mod tests {
stateful_table.page_up();
assert_some_eq_x!(
stateful_table.filtered_state.as_ref().unwrap().selected(),
stateful_table.filtered_items.as_ref().unwrap().len() - 21
);
stateful_table.page_down();
assert_some_eq_x!(
stateful_table.filtered_state.as_ref().unwrap().selected(),
0
);
stateful_table.scroll_down();
stateful_table.page_up();
assert_some_eq_x!(
stateful_table.filtered_state.as_ref().unwrap().selected(),
stateful_table.filtered_items.as_ref().unwrap().len() - 20
);
stateful_table.scroll_down();
stateful_table.page_down();
assert_some_eq_x!(
stateful_table.filtered_state.as_ref().unwrap().selected(),
2
);
}
#[test]
@@ -376,33 +416,105 @@ mod tests {
assert_some_eq_x!(stateful_table.state.selected(), 20);
stateful_table.page_down();
assert_some_eq_x!(stateful_table.state.selected(), 40);
stateful_table.page_down();
assert_some_eq_x!(stateful_table.state.selected(), 60);
stateful_table.page_down();
assert_some_eq_x!(stateful_table.state.selected(), 80);
stateful_table.page_down();
assert_some_eq_x!(stateful_table.state.selected(), 99);
stateful_table.page_down();
assert_some_eq_x!(stateful_table.state.selected(), 99);
stateful_table.page_up();
assert_some_eq_x!(stateful_table.state.selected(), 79);
stateful_table.page_up();
assert_some_eq_x!(stateful_table.state.selected(), 59);
stateful_table.page_up();
assert_some_eq_x!(stateful_table.state.selected(), 39);
stateful_table.page_up();
assert_some_eq_x!(stateful_table.state.selected(), 19);
stateful_table.page_up();
assert_some_eq_x!(stateful_table.state.selected(), 0);
stateful_table.page_up();
assert_some_eq_x!(
stateful_table.state.selected(),
stateful_table.items.len() - 21
);
assert_some_eq_x!(stateful_table.state.selected(), 0);
}
stateful_table.page_down();
#[test]
fn test_stateful_table_pagination_on_twenty_one_item_table_stops_at_last_item() {
let mut stateful_table = StatefulTable::default();
stateful_table.set_items((0..21).collect());
assert_some_eq_x!(stateful_table.state.selected(), 0);
stateful_table.scroll_down();
stateful_table.page_up();
assert_some_eq_x!(
stateful_table.state.selected(),
stateful_table.items.len() - 20
);
stateful_table.scroll_down();
stateful_table.page_down();
assert_some_eq_x!(stateful_table.state.selected(), 2);
assert_some_eq_x!(stateful_table.state.selected(), 20);
stateful_table.page_down();
assert_some_eq_x!(stateful_table.state.selected(), 20);
stateful_table.page_up();
assert_some_eq_x!(stateful_table.state.selected(), 0);
}
#[test]
fn test_stateful_table_pagination_on_five_item_table_stops_at_last_item() {
let mut stateful_table = StatefulTable::default();
stateful_table.set_items((0..5).collect());
assert_some_eq_x!(stateful_table.state.selected(), 0);
stateful_table.page_down();
assert_some_eq_x!(stateful_table.state.selected(), 4);
stateful_table.page_down();
assert_some_eq_x!(stateful_table.state.selected(), 4);
stateful_table.page_up();
assert_some_eq_x!(stateful_table.state.selected(), 0);
}
#[test]
fn test_stateful_table_pagination_on_four_item_table_reaches_last_item() {
let mut stateful_table = StatefulTable::default();
stateful_table.set_items((0..4).collect());
assert_some_eq_x!(stateful_table.state.selected(), 0);
stateful_table.page_down();
assert_some_eq_x!(stateful_table.state.selected(), 3);
stateful_table.page_up();
assert_some_eq_x!(stateful_table.state.selected(), 0);
}
#[test]
@@ -3,15 +3,15 @@
pub mod test_utils {
use crate::models::lidarr_models::{
AddArtistSearchResult, Album, AlbumStatistics, Artist, ArtistStatistics, ArtistStatus,
AudioTags, BlocklistItem, BlocklistResponse, DownloadRecord, DownloadStatus, DownloadsResponse,
AudioTags, BlocklistItem, BlocklistResponse, DownloadRecord, DownloadsResponse,
EditArtistParams, LidarrHistoryData, LidarrHistoryEventType, LidarrHistoryItem,
LidarrHistoryWrapper, LidarrRelease, LidarrTask, LidarrTaskName, MediaInfo, Member,
NewItemMonitorType, Ratings, Track, TrackFile,
};
use crate::models::servarr_models::IndexerSettings;
use crate::models::servarr_models::{
Indexer, IndexerField, MetadataProfile, Quality, QualityProfile, QualityWrapper, RootFolder,
SystemStatus, Tag,
DownloadStatus, Indexer, IndexerField, MetadataProfile, Quality, QualityProfile,
QualityWrapper, RootFolder, SystemStatus, Tag,
};
use crate::models::{HorizontallyScrollableText, ScrollableText};
use bimap::BiMap;
@@ -282,11 +282,11 @@ pub mod test_utils {
pub fn lidarr_history_item() -> LidarrHistoryItem {
LidarrHistoryItem {
id: 1,
id: 13,
source_title: "Test source title".into(),
album_id: 1,
artist_id: 1,
track_id: 1,
artist_id: 3,
track_id: 5,
quality: quality_wrapper(),
date: DateTime::from(DateTime::parse_from_rfc3339("2023-01-01T00:00:00Z").unwrap()),
event_type: LidarrHistoryEventType::Grabbed,
@@ -459,7 +459,7 @@ pub mod test_utils {
pub fn track_file() -> TrackFile {
TrackFile {
id: 1,
id: 7,
path: "/music/P!nk/TRUSTFALL/01 - When I Get There.flac".to_string(),
size: 39216378,
quality: quality_wrapper(),
@@ -471,10 +471,10 @@ pub mod test_utils {
pub fn track() -> Track {
Track {
id: 1,
artist_id: 1,
id: 5,
artist_id: 3,
foreign_track_id: "test-foreign-track-id".to_string(),
track_file_id: 1,
track_file_id: 7,
album_id: 1,
explicit: false,
track_number: "1".to_string(),
@@ -91,7 +91,7 @@ mod tests {
"tmdbId": 1234,
"originalLanguage": {"id": 1, "name": "English"},
"sizeOnDisk": 3543348019i64,
"status": "Downloaded",
"status": "released",
"overview": "Blah blah blah",
"path": "/nfs/movies",
"studio": "21st Century Alex",
@@ -127,7 +127,7 @@ mod tests {
"tmdbId": 1234,
"originalLanguage": {"id": 1, "name": "English"},
"sizeOnDisk": 3543348019i64,
"status": "Downloaded",
"status": "released",
"overview": "Blah blah blah",
"path": "/nfs/movies",
"studio": "21st Century Alex",
@@ -230,7 +230,7 @@ mod tests {
"tmdbId": 1234,
"originalLanguage": {"id": 1, "name": "English"},
"sizeOnDisk": 3543348019i64,
"status": "Downloaded",
"status": "released",
"overview": "Blah blah blah",
"path": "/nfs/movies",
"studio": "21st Century Alex",
@@ -266,7 +266,7 @@ mod tests {
"tmdbId": 1234,
"originalLanguage": {"id": 1, "name": "English"},
"sizeOnDisk": 3543348019i64,
"status": "Downloaded",
"status": "released",
"overview": "Blah blah blah",
"path": "/nfs/movies",
"studio": "21st Century Alex",
+2 -1
View File
@@ -1,11 +1,12 @@
use crate::models::radarr_models::{
AddMovieBody, AddMovieSearchResult, Credit, CreditType, DeleteMovieParams, DownloadRecord,
DownloadStatus, EditMovieParams, Movie, MovieCommandBody, MovieHistoryItem, RadarrRelease,
EditMovieParams, Movie, MovieCommandBody, MovieHistoryItem, RadarrRelease,
RadarrReleaseDownloadBody,
};
use crate::models::servarr_data::Notification;
use crate::models::servarr_data::radarr::modals::MovieDetailsModal;
use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock;
use crate::models::servarr_models::DownloadStatus;
use crate::models::stateful_table::StatefulTable;
use crate::models::{Route, ScrollableText};
use crate::network::radarr_network::RadarrEvent;
@@ -1,13 +1,13 @@
#[cfg(test)]
mod tests {
use crate::models::radarr_models::{
AddMovieBody, AddMovieOptions, Credit, DeleteMovieParams, DownloadRecord, DownloadStatus,
EditMovieParams, MinimumAvailability, Movie, MovieHistoryItem, MovieMonitor,
RadarrReleaseDownloadBody,
AddMovieBody, AddMovieOptions, Credit, DeleteMovieParams, DownloadRecord, EditMovieParams,
MinimumAvailability, Movie, MovieHistoryItem, MovieMonitor, RadarrReleaseDownloadBody,
};
use crate::models::servarr_data::Notification;
use crate::models::servarr_data::radarr::modals::MovieDetailsModal;
use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock;
use crate::models::servarr_models::DownloadStatus;
use crate::models::stateful_table::SortOption;
use crate::network::NetworkResource;
use crate::network::network_tests::test_utils::{MockServarrApi, test_network};
@@ -701,7 +701,7 @@ mod tests {
"name": "English"
},
"sizeOnDisk": 0,
"status": "Downloaded",
"status": "released",
"overview": "Blah blah blah",
"path": "/nfs/movies",
"studio": "21st Century Alex",
@@ -2,13 +2,13 @@
pub mod test_utils {
use crate::models::radarr_models::{
AddMovieSearchResult, BlocklistItem, BlocklistItemMovie, Collection, CollectionMovie, Credit,
CreditType, DownloadRecord, DownloadStatus, DownloadsResponse, IndexerSettings, MediaInfo,
MinimumAvailability, Movie, MovieCollection, MovieFile, MovieHistoryItem, RadarrHistoryData,
CreditType, DownloadRecord, DownloadsResponse, IndexerSettings, MediaInfo, MinimumAvailability,
Movie, MovieCollection, MovieFile, MovieHistoryItem, MovieStatus, RadarrHistoryData,
RadarrHistoryEventType, RadarrHistoryItem, RadarrRelease, RadarrTask, RadarrTaskName, Rating,
RatingsList,
};
use crate::models::servarr_models::{
Indexer, IndexerField, Language, Quality, QualityWrapper, RootFolder,
DownloadStatus, Indexer, IndexerField, Language, Quality, QualityWrapper, RootFolder,
};
use crate::models::{HorizontallyScrollableText, ScrollableText};
use bimap::BiMap;
@@ -25,7 +25,7 @@ pub mod test_utils {
"name": "English"
},
"sizeOnDisk": 3543348019,
"status": "Downloaded",
"status": "released",
"overview": "Blah blah blah",
"path": "/nfs/movies",
"studio": "21st Century Alex",
@@ -234,7 +234,7 @@ pub mod test_utils {
title: "Test".to_owned().into(),
original_language: language(),
size_on_disk: 3543348019,
status: "Downloaded".to_owned(),
status: MovieStatus::Released,
overview: "Blah blah blah".to_owned(),
path: "/nfs/movies".to_owned(),
studio: Some("21st Century Alex".to_owned()),
@@ -300,7 +300,7 @@ pub mod test_utils {
tmdb_id: 1234,
title: HorizontallyScrollableText::from("Test"),
original_language: language(),
status: "released".to_owned(),
status: MovieStatus::Released,
overview: "New movie blah blah blah".to_owned(),
genres: genres(),
year: 2023,
@@ -4,13 +4,12 @@ pub mod test_utils {
use crate::models::HorizontallyScrollableText;
use crate::models::readarr_models::{
AddAuthorBody, AddAuthorOptions, AddAuthorSearchResult, Author, AuthorStatus, BlocklistItem,
Book, BookFile, DownloadRecord, DownloadStatus, DownloadsResponse, Edition, MonitorType,
NewItemMonitorType, ReadarrHistoryData, ReadarrHistoryEventType, ReadarrHistoryItem,
ReadarrRelease,
Book, BookFile, DownloadRecord, DownloadsResponse, Edition, MonitorType, NewItemMonitorType,
ReadarrHistoryData, ReadarrHistoryEventType, ReadarrHistoryItem, ReadarrRelease,
};
use crate::models::servarr_data::modals::IndexerTestResultModalItem;
use crate::models::servarr_models::{
Indexer, IndexerField, IndexerSettings, Quality, QualityWrapper, RootFolder,
DownloadStatus, Indexer, IndexerField, IndexerSettings, Quality, QualityWrapper, RootFolder,
};
use chrono::DateTime;
use serde_json::{Number, json};
@@ -41,8 +41,8 @@ mod tests {
"records": [{
"title": "Test Download Title",
"status": "downloading",
"id": 1,
"episodeId": 1,
"id": 9,
"episodeId": 11,
"size": 3543348019f64,
"sizeleft": 1771674009f64,
"outputPath": "/nfs/tv/Test show/season 1/",
@@ -1,8 +1,8 @@
use crate::models::servarr_data::sonarr::modals::SeasonDetailsModal;
use crate::models::servarr_data::sonarr::sonarr_data::ActiveSonarrBlock;
use crate::models::servarr_models::Language;
use crate::models::servarr_models::{DownloadStatus, Language};
use crate::models::sonarr_models::{
DownloadRecord, DownloadStatus, Episode, EpisodeFile, MonitorEpisodeBody, SonarrCommandBody,
DownloadRecord, Episode, EpisodeFile, MonitorEpisodeBody, SonarrCommandBody,
SonarrHistoryWrapper, SonarrRelease,
};
use crate::models::{Route, ScrollableText};
@@ -2,8 +2,9 @@
mod tests {
use crate::models::servarr_data::sonarr::modals::{EpisodeDetailsModal, SeasonDetailsModal};
use crate::models::servarr_data::sonarr::sonarr_data::ActiveSonarrBlock;
use crate::models::servarr_models::DownloadStatus;
use crate::models::sonarr_models::{
DownloadRecord, DownloadStatus, Episode, MonitorEpisodeBody, Season, Series, SonarrHistoryItem,
DownloadRecord, Episode, MonitorEpisodeBody, Season, Series, SonarrHistoryItem,
SonarrHistoryWrapper, SonarrRelease, SonarrSerdeable,
};
use crate::models::stateful_table::SortOption;
@@ -69,7 +70,7 @@ mod tests {
..episode()
};
let expected_episodes = vec![episode_1.clone(), episode_2.clone(), episode_3.clone()];
let mut expected_sorted_episodes = vec![episode_1.clone(), episode_3.clone()];
let mut expected_sorted_episodes = vec![episode_3.clone(), episode_1.clone()];
let (mock, app, _server) = MockServarrApi::get()
.query("seriesId=1")
.returns(json!([episode_1, episode_2, episode_3]))
@@ -175,6 +176,7 @@ mod tests {
..episode()
};
let expected_episodes = vec![episode_1.clone(), episode_2.clone(), episode_3.clone()];
let expected_sorted_episodes = vec![episode_2.clone(), episode_3.clone(), episode_1.clone()];
let (mock, app, _server) = MockServarrApi::get()
.query("seriesId=1")
.returns(json!([episode_1, episode_2, episode_3]))
@@ -216,7 +218,7 @@ mod tests {
.unwrap()
.episodes
.items,
expected_episodes
expected_sorted_episodes
);
assert!(
app
@@ -294,10 +296,10 @@ mod tests {
"monitored": true
},
{
"id": 1,
"id": 11,
"seriesId": 1,
"tvdbId": 1234,
"episodeFileId": 1,
"episodeFileId": 7,
"seasonNumber": 1,
"episodeNumber": 1,
"title": "Something cool",
@@ -1,13 +1,13 @@
#[cfg(test)]
pub mod test_utils {
use crate::models::servarr_models::{
Indexer, IndexerField, Language, Quality, QualityWrapper, RootFolder,
DownloadStatus, Indexer, IndexerField, Language, Quality, QualityWrapper, RootFolder,
};
use crate::models::sonarr_models::{
AddSeriesSearchResult, AddSeriesSearchResultStatistics, BlocklistItem, DownloadRecord,
DownloadStatus, DownloadsResponse, Episode, EpisodeFile, MediaInfo, Rating, Season,
SeasonStatistics, Series, SeriesStatistics, SeriesStatus, SeriesType, SonarrHistoryData,
SonarrHistoryEventType, SonarrHistoryItem, SonarrRelease, SonarrTask, SonarrTaskName,
DownloadsResponse, Episode, EpisodeFile, MediaInfo, Rating, Season, SeasonStatistics, Series,
SeriesStatistics, SeriesStatus, SeriesType, SonarrHistoryData, SonarrHistoryEventType,
SonarrHistoryItem, SonarrRelease, SonarrTask, SonarrTaskName,
};
use crate::models::{HorizontallyScrollableText, ScrollableText};
use bimap::BiMap;
@@ -64,14 +64,14 @@ pub mod test_utils {
pub const EPISODE_JSON: &str = r#"{
"seriesId": 1,
"tvdbId": 1234,
"episodeFileId": 1,
"episodeFileId": 7,
"seasonNumber": 1,
"episodeNumber": 1,
"title": "Something cool",
"airDateUtc": "2024-02-10T07:28:45Z",
"overview": "Okay so this one time at band camp...",
"episodeFile": {
"id": 1,
"id": 7,
"relativePath": "/season 1/episode 1.mkv",
"path": "/nfs/tv/series/season 1/episode 1.mkv",
"size": 3543348019,
@@ -96,7 +96,7 @@ pub mod test_utils {
},
"hasFile": true,
"monitored": true,
"id": 1
"id": 11
}"#;
pub fn add_series_search_result() -> AddSeriesSearchResult {
@@ -139,8 +139,8 @@ pub mod test_utils {
DownloadRecord {
title: "Test Download Title".to_owned(),
status: DownloadStatus::Downloading,
id: 1,
episode_id: Some(Number::from(1i64)),
id: 9,
episode_id: Some(Number::from(11i64)),
size: 3543348019f64,
sizeleft: 1771674009f64,
output_path: Some(HorizontallyScrollableText::from(
@@ -159,10 +159,10 @@ pub mod test_utils {
pub fn episode() -> Episode {
Episode {
id: 1,
id: 11,
series_id: 1,
tvdb_id: 1234,
episode_file_id: 1,
episode_file_id: 7,
season_number: 1,
episode_number: 1,
title: "Something cool".to_owned(),
@@ -178,7 +178,7 @@ pub mod test_utils {
pub fn episode_file() -> EpisodeFile {
EpisodeFile {
id: 1,
id: 7,
relative_path: "/season 1/episode 1.mkv".to_owned(),
path: "/nfs/tv/series/season 1/episode 1.mkv".to_owned(),
size: 3543348019,
+2 -3
View File
@@ -1,9 +1,8 @@
use crate::app::App;
use crate::models::Route;
use crate::models::lidarr_models::{
DownloadRecord, DownloadStatus, LidarrHistoryItem, LidarrRelease, Track,
};
use crate::models::lidarr_models::{DownloadRecord, LidarrHistoryItem, LidarrRelease, Track};
use crate::models::servarr_data::lidarr::lidarr_data::{ALBUM_DETAILS_BLOCKS, ActiveLidarrBlock};
use crate::models::servarr_models::DownloadStatus;
use crate::ui::lidarr_ui::library::track_details_ui::TrackDetailsUi;
use crate::ui::lidarr_ui::lidarr_ui_utils::create_history_event_details;
use crate::ui::styles::{ManagarrStyle, secondary_style};
@@ -6,10 +6,11 @@ mod tests {
use strum::IntoEnumIterator;
use crate::app::App;
use crate::models::lidarr_models::{DownloadRecord, DownloadStatus, Track};
use crate::models::lidarr_models::{DownloadRecord, Track};
use crate::models::servarr_data::lidarr::lidarr_data::{
ALBUM_DETAILS_BLOCKS, ActiveLidarrBlock, TRACK_DETAILS_BLOCKS,
};
use crate::models::servarr_models::DownloadStatus;
use crate::models::stateful_table::StatefulTable;
use crate::network::lidarr_network::lidarr_network_test_utils::test_utils::download_record;
use crate::ui::DrawUi;
@@ -137,6 +138,80 @@ mod tests {
assert_eq!(style, row.downloaded());
}
mod test_track_row_styling {
use pretty_assertions::assert_eq;
use ratatui::style::Style;
use crate::network::lidarr_network::lidarr_network_test_utils::test_utils::track;
use crate::ui::styles::{downloading_style, missing_style};
use crate::ui::ui_test_utils::test_utils::{TerminalSize, create_test_terminal};
use super::*;
#[test]
fn test_album_details_ui_renders_downloading_track_with_downloading_style() {
let mut app = App::test_default_fully_populated();
app.push_navigation_stack(ActiveLidarrBlock::AlbumDetails.into());
set_tracks_with_unselected_probe(&mut app);
let style = rendered_row_style(&mut app, "Unselected track");
assert_eq!(style.fg, downloading_style().fg);
}
#[test]
fn test_album_details_ui_renders_missing_track_with_missing_style_when_queue_is_empty() {
let mut app = App::test_default_fully_populated();
app.push_navigation_stack(ActiveLidarrBlock::AlbumDetails.into());
app.data.lidarr_data.downloads.set_items(vec![]);
set_tracks_with_unselected_probe(&mut app);
let style = rendered_row_style(&mut app, "Unselected track");
assert_eq!(style.fg, missing_style().fg);
}
fn set_tracks_with_unselected_probe(app: &mut App<'_>) {
let album_details_modal = app.data.lidarr_data.album_details_modal.as_mut().unwrap();
album_details_modal.album_details_tabs.set_index(0);
album_details_modal.tracks.set_items(vec![
track(),
Track {
has_file: false,
title: "Unselected track".to_owned(),
..track()
},
]);
}
fn rendered_row_style(app: &mut App<'_>, needle: &str) -> Style {
let (width, height) = TerminalSize::Large.to_cartesian();
let mut terminal = create_test_terminal(width, height);
terminal
.draw(|f| {
AlbumDetailsUi::draw(f, app, f.area());
})
.unwrap();
let buffer = terminal.backend().buffer();
for y in 0..height {
let row = (0..width)
.map(|x| buffer.cell((x, y)).expect("a rendered cell").symbol())
.collect::<String>();
if let Some(byte_index) = row.find(needle) {
let column = row[..byte_index].chars().count() as u16;
return buffer.cell((column, y)).expect("a rendered cell").style();
}
}
panic!("no rendered row contained {needle}");
}
}
mod snapshot_tests {
use crate::ui::ui_test_utils::test_utils::TerminalSize;
use rstest::rstest;
@@ -29,6 +29,80 @@ mod tests {
});
}
mod test_movie_row_styling {
use pretty_assertions::assert_eq;
use ratatui::style::Style;
use crate::models::radarr_models::Movie;
use crate::network::radarr_network::radarr_network_test_utils::test_utils::movie;
use crate::ui::styles::{downloading_style, missing_style};
use crate::ui::ui_test_utils::test_utils::create_test_terminal;
use super::*;
#[test]
fn test_library_ui_renders_downloading_movie_with_downloading_style() {
let mut app = App::test_default_fully_populated();
app.push_navigation_stack(ActiveRadarrBlock::Movies.into());
set_movies_with_unselected_probe(&mut app);
let style = rendered_row_style(&mut app, "Unselected movie");
assert_eq!(style.fg, downloading_style().fg);
}
#[test]
fn test_library_ui_renders_missing_movie_with_missing_style_when_queue_is_empty() {
let mut app = App::test_default_fully_populated();
app.push_navigation_stack(ActiveRadarrBlock::Movies.into());
app.data.radarr_data.downloads.set_items(vec![]);
set_movies_with_unselected_probe(&mut app);
let style = rendered_row_style(&mut app, "Unselected movie");
assert_eq!(style.fg, missing_style().fg);
}
fn set_movies_with_unselected_probe(app: &mut App<'_>) {
app.data.radarr_data.movies.set_items(vec![
movie(),
Movie {
has_file: false,
monitored: true,
title: "Unselected movie".into(),
..movie()
},
]);
}
fn rendered_row_style(app: &mut App<'_>, needle: &str) -> Style {
let (width, height) = TerminalSize::Large.to_cartesian();
let mut terminal = create_test_terminal(width, height);
terminal
.draw(|f| {
LibraryUi::draw(f, app, f.area());
})
.unwrap();
let buffer = terminal.backend().buffer();
for y in 0..height {
let row = (0..width)
.map(|x| buffer.cell((x, y)).expect("a rendered cell").symbol())
.collect::<String>();
if let Some(byte_index) = row.find(needle) {
let column = row[..byte_index].chars().count() as u16;
return buffer.cell((column, y)).expect("a rendered cell").style();
}
}
panic!("no rendered row contained {needle}");
}
}
mod snapshot_tests {
use super::*;
use crate::models::BlockSelectionState;
+9 -11
View File
@@ -13,7 +13,7 @@ use serde_json::Number;
use crate::app::App;
use crate::models::Route;
use crate::models::radarr_models::{Credit, MovieHistoryItem, RadarrRelease};
use crate::models::radarr_models::{Credit, MovieHistoryItem, MovieStatus, RadarrRelease};
use crate::models::servarr_data::radarr::modals::MovieDetailsModal;
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, MOVIE_DETAILS_BLOCKS};
use crate::ui::styles::ManagarrStyle;
@@ -179,13 +179,7 @@ fn draw_movie_details(f: &mut Frame<'_>, app: &App<'_>, area: Rect) {
match app.data.radarr_data.movie_details_modal.as_ref() {
Some(movie_details_modal) if !app.is_loading => {
let is_monitored = app.data.radarr_data.movies.current_selection().monitored;
let status = app
.data
.radarr_data
.movies
.current_selection()
.status
.clone();
let status = app.data.radarr_data.movies.current_selection().status;
let movie_details = &movie_details_modal.movie_details;
let download_status = movie_details
.items
@@ -201,7 +195,7 @@ fn draw_movie_details(f: &mut Frame<'_>, app: &App<'_>, area: Rect) {
.map(|line| {
let split = line.split(':').collect::<Vec<&str>>();
let title = format!("{}:", split[0]);
let style = style_from_download_status(download_status, is_monitored, status.clone());
let style = style_from_download_status(download_status, is_monitored, status);
Line::from(vec![
Span::styled(title, style.bold()),
@@ -531,13 +525,17 @@ fn draw_manual_search_confirm_prompt(f: &mut Frame<'_>, app: &mut App<'_>) {
}
}
fn style_from_download_status(download_status: &str, is_monitored: bool, status: String) -> Style {
fn style_from_download_status(
download_status: &str,
is_monitored: bool,
status: MovieStatus,
) -> Style {
match download_status {
"Downloaded" => downloaded_style(),
"Awaiting Import" => awaiting_import_style(),
"Downloading" => downloading_style(),
_ if !is_monitored && download_status == "Missing" => unmonitored_missing_style(),
_ if status != "released" && download_status == "Missing" => unreleased_style(),
_ if status != MovieStatus::Released && download_status == "Missing" => unreleased_style(),
"Missing" => missing_style(),
_ => downloaded_style(),
}
@@ -6,6 +6,7 @@ mod tests {
use strum::IntoEnumIterator;
use crate::app::App;
use crate::models::radarr_models::MovieStatus;
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, MOVIE_DETAILS_BLOCKS};
use crate::ui::DrawUi;
use crate::ui::radarr_ui::library::movie_details_ui::{
@@ -13,7 +14,7 @@ mod tests {
};
use crate::ui::styles::{
awaiting_import_style, downloaded_style, downloading_style, missing_style,
unmonitored_missing_style,
unmonitored_missing_style, unreleased_style,
};
use crate::ui::ui_test_utils::test_utils::{TerminalSize, render_to_string_with_app};
@@ -29,21 +30,26 @@ mod tests {
}
#[rstest]
#[case("Downloading", true, "", downloading_style())]
#[case("Downloaded", true, "", downloaded_style())]
#[case("Awaiting Import", true, "", awaiting_import_style())]
#[case("Missing", false, "", unmonitored_missing_style())]
#[case("Missing", false, "", unmonitored_missing_style())]
#[case("Missing", true, "released", missing_style())]
#[case("", true, "", downloaded_style())]
#[case("Downloading", true, MovieStatus::Released, downloading_style())]
#[case("Downloaded", true, MovieStatus::Released, downloaded_style())]
#[case(
"Awaiting Import",
true,
MovieStatus::Released,
awaiting_import_style()
)]
#[case("Missing", false, MovieStatus::InCinemas, unmonitored_missing_style())]
#[case("Missing", true, MovieStatus::InCinemas, unreleased_style())]
#[case("Missing", true, MovieStatus::Released, missing_style())]
#[case("", true, MovieStatus::Released, downloaded_style())]
fn test_style_from_download_status(
#[case] download_status: &str,
#[case] is_monitored: bool,
#[case] movie_status: &str,
#[case] movie_status: MovieStatus,
#[case] expected_style: Style,
) {
assert_eq!(
style_from_download_status(download_status, is_monitored, movie_status.to_owned()),
style_from_download_status(download_status, is_monitored, movie_status),
expected_style
);
}
+3 -3
View File
@@ -1,9 +1,9 @@
use crate::app::App;
use crate::logos::RADARR_LOGO;
use crate::models::Route;
use crate::models::radarr_models::{DownloadRecord, DownloadStatus, Movie};
use crate::models::radarr_models::{DownloadRecord, Movie, MovieStatus};
use crate::models::servarr_data::radarr::radarr_data::RadarrData;
use crate::models::servarr_models::{DiskSpace, RootFolder};
use crate::models::servarr_models::{DiskSpace, DownloadStatus, RootFolder};
use crate::ui::DrawUi;
use crate::ui::draw_tabs;
use crate::ui::radarr_ui::blocklist::BlocklistUi;
@@ -243,7 +243,7 @@ fn decorate_with_row_style<'a>(
return row.unmonitored_missing();
}
if movie.status != "released" {
if movie.status != MovieStatus::Released {
return row.unreleased();
}
+16 -9
View File
@@ -1,6 +1,7 @@
#[cfg(test)]
mod tests {
use crate::models::radarr_models::{DownloadRecord, DownloadStatus, Movie};
use crate::models::radarr_models::{DownloadRecord, Movie, MovieStatus};
use crate::models::servarr_models::DownloadStatus;
use pretty_assertions::assert_eq;
use ratatui::widgets::{Cell, Row};
use rstest::rstest;
@@ -54,26 +55,32 @@ mod tests {
false,
Some(DownloadStatus::Downloading),
false,
"",
MovieStatus::Released,
RowStyle::Downloading
)]
#[case(
false,
Some(DownloadStatus::Completed),
false,
"",
MovieStatus::Released,
RowStyle::AwaitingImport
)]
#[case(false, None, false, "", RowStyle::UnmonitoredMissing)]
#[case(false, None, true, "", RowStyle::Unreleased)]
#[case(false, None, true, "released", RowStyle::Missing)]
#[case(true, None, false, "", RowStyle::Unmonitored)]
#[case(true, None, true, "", RowStyle::Downloaded)]
#[case(
false,
None,
false,
MovieStatus::Released,
RowStyle::UnmonitoredMissing
)]
#[case(false, None, true, MovieStatus::InCinemas, RowStyle::Unreleased)]
#[case(false, None, true, MovieStatus::Released, RowStyle::Missing)]
#[case(true, None, false, MovieStatus::Released, RowStyle::Unmonitored)]
#[case(true, None, true, MovieStatus::Released, RowStyle::Downloaded)]
fn test_decorate_with_row_style(
#[case] has_file: bool,
#[case] download_status: Option<DownloadStatus>,
#[case] is_monitored: bool,
#[case] movie_status: String,
#[case] movie_status: MovieStatus,
#[case] expected_style: RowStyle,
) {
let downloads_vec = if let Some(download_status) = download_status {
@@ -23,7 +23,8 @@ mod tests {
use rstest::rstest;
use serde_json::Number;
use crate::models::readarr_models::{DownloadRecord, DownloadStatus};
use crate::models::readarr_models::DownloadRecord;
use crate::models::servarr_models::DownloadStatus;
use crate::ui::ui_test_utils::test_utils::TerminalSize;
use super::*;
@@ -1,10 +1,8 @@
use crate::app::App;
use crate::models::Route;
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, EPISODE_DETAILS_BLOCKS};
use crate::models::servarr_models::Language;
use crate::models::sonarr_models::{
DownloadRecord, DownloadStatus, Episode, SonarrHistoryItem, SonarrRelease,
};
use crate::models::servarr_models::{DownloadStatus, Language};
use crate::models::sonarr_models::{DownloadRecord, Episode, SonarrHistoryItem, SonarrRelease};
use crate::ui::sonarr_ui::sonarr_ui_utils::create_history_event_details;
use crate::ui::styles::ManagarrStyle;
use crate::ui::styles::{
@@ -8,7 +8,8 @@ mod tests {
use crate::models::servarr_data::sonarr::sonarr_data::{
ActiveSonarrBlock, EPISODE_DETAILS_BLOCKS,
};
use crate::models::sonarr_models::{DownloadRecord, DownloadStatus, Episode};
use crate::models::servarr_models::DownloadStatus;
use crate::models::sonarr_models::{DownloadRecord, Episode};
use crate::ui::DrawUi;
use crate::ui::sonarr_ui::library::episode_details_ui::{EpisodeDetailsUi, style_from_status};
use crate::ui::styles::{
@@ -1,9 +1,8 @@
use crate::app::App;
use crate::models::Route;
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, SEASON_DETAILS_BLOCKS};
use crate::models::sonarr_models::{
DownloadRecord, DownloadStatus, Episode, SonarrHistoryItem, SonarrRelease,
};
use crate::models::servarr_models::DownloadStatus;
use crate::models::sonarr_models::{DownloadRecord, Episode, SonarrHistoryItem, SonarrRelease};
use crate::ui::sonarr_ui::library::episode_details_ui::EpisodeDetailsUi;
use crate::ui::sonarr_ui::sonarr_ui_utils::create_history_event_details;
use crate::ui::styles::ManagarrStyle;
@@ -10,7 +10,8 @@ mod tests {
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::servarr_models::DownloadStatus;
use crate::models::sonarr_models::{DownloadRecord, Episode};
use crate::models::stateful_table::StatefulTable;
use crate::network::sonarr_network::sonarr_network_test_utils::test_utils::download_record;
use crate::ui::DrawUi;
@@ -263,6 +264,81 @@ mod tests {
assert_eq!(style, row.downloaded());
}
mod test_episode_row_styling {
use pretty_assertions::assert_eq;
use ratatui::style::Style;
use crate::network::sonarr_network::sonarr_network_test_utils::test_utils::episode;
use crate::ui::styles::{downloading_style, missing_style};
use crate::ui::ui_test_utils::test_utils::{TerminalSize, create_test_terminal};
use super::*;
#[test]
fn test_season_details_ui_renders_downloading_episode_with_downloading_style() {
let mut app = App::test_default_fully_populated();
app.push_navigation_stack(ActiveSonarrBlock::SeasonDetails.into());
set_episodes_with_unselected_probe(&mut app);
let style = rendered_row_style(&mut app, "Unselected episode");
assert_eq!(style.fg, downloading_style().fg);
}
#[test]
fn test_season_details_ui_renders_missing_episode_with_missing_style_when_queue_is_empty() {
let mut app = App::test_default_fully_populated();
app.push_navigation_stack(ActiveSonarrBlock::SeasonDetails.into());
app.data.sonarr_data.downloads.set_items(vec![]);
set_episodes_with_unselected_probe(&mut app);
let style = rendered_row_style(&mut app, "Unselected episode");
assert_eq!(style.fg, missing_style().fg);
}
fn set_episodes_with_unselected_probe(app: &mut App<'_>) {
let season_details_modal = app.data.sonarr_data.season_details_modal.as_mut().unwrap();
season_details_modal.season_details_tabs.set_index(0);
season_details_modal.episodes.set_items(vec![
episode(),
Episode {
has_file: false,
monitored: true,
title: "Unselected episode".to_owned(),
..episode()
},
]);
}
fn rendered_row_style(app: &mut App<'_>, needle: &str) -> Style {
let (width, height) = TerminalSize::Large.to_cartesian();
let mut terminal = create_test_terminal(width, height);
terminal
.draw(|f| {
SeasonDetailsUi::draw(f, app, f.area());
})
.unwrap();
let buffer = terminal.backend().buffer();
for y in 0..height {
let row = (0..width)
.map(|x| buffer.cell((x, y)).expect("a rendered cell").symbol())
.collect::<String>();
if let Some(byte_index) = row.find(needle) {
let column = row[..byte_index].chars().count() as u16;
return buffer.cell((column, y)).expect("a rendered cell").style();
}
}
panic!("no rendered row contained {needle}");
}
}
mod snapshot_tests {
use crate::ui::ui_test_utils::test_utils::TerminalSize;
use rstest::rstest;