feat(cli): Sonarr support for fetching season history events

This commit is contained in:
2024-12-09 14:30:07 -07:00
parent 5b65e87225
commit 6427a80bd1
4 changed files with 126 additions and 15 deletions
+29
View File
@@ -67,6 +67,23 @@ pub enum SonarrListCommand {
QueuedEvents, QueuedEvents,
#[command(about = "List all root folders in Sonarr")] #[command(about = "List all root folders in Sonarr")]
RootFolders, RootFolders,
#[command(
about = "Fetch all history events for the given season corresponding to the series with the given ID."
)]
SeasonHistory {
#[arg(
long,
help = "The Sonarr ID of the series whose history you wish to fetch and list",
required = true
)]
series_id: i64,
#[arg(
long,
help = "The season number to fetch history events for",
required = true
)]
season_number: i64,
},
#[command(about = "List all series in your Sonarr library")] #[command(about = "List all series in your Sonarr library")]
Series, Series,
#[command(about = "Fetch all history events for the series with the given ID")] #[command(about = "Fetch all history events for the series with the given ID")]
@@ -207,6 +224,18 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, SonarrListCommand> for SonarrListCommandH
.await?; .await?;
serde_json::to_string_pretty(&resp)? serde_json::to_string_pretty(&resp)?
} }
SonarrListCommand::SeasonHistory {
series_id,
season_number,
} => {
let resp = self
.network
.handle_network_event(
SonarrEvent::GetSeasonHistory(Some((series_id, season_number))).into(),
)
.await?;
serde_json::to_string_pretty(&resp)?
}
SonarrListCommand::Series => { SonarrListCommand::Series => {
let resp = self let resp = self
.network .network
@@ -149,6 +149,58 @@ mod tests {
} }
} }
#[test]
fn test_season_history_requires_series_id() {
let result = Cli::command().try_get_matches_from([
"managarr",
"sonarr",
"list",
"season-history",
"--season-number",
"1",
]);
assert!(result.is_err());
assert_eq!(
result.unwrap_err().kind(),
ErrorKind::MissingRequiredArgument
);
}
#[test]
fn test_season_history_requires_season_number() {
let result = Cli::command().try_get_matches_from([
"managarr",
"sonarr",
"list",
"season-history",
"--series-id",
"1",
]);
assert!(result.is_err());
assert_eq!(
result.unwrap_err().kind(),
ErrorKind::MissingRequiredArgument
);
}
#[test]
fn test_season_history_requirements_satisfied() {
let result = Cli::command().try_get_matches_from([
"managarr",
"sonarr",
"list",
"season-history",
"--series-id",
"1",
"--season-number",
"1",
]);
assert!(result.is_ok());
}
#[test] #[test]
fn test_list_series_history_requires_series_id() { fn test_list_series_history_requires_series_id() {
let result = let result =
@@ -368,5 +420,35 @@ mod tests {
assert!(result.is_ok()); assert!(result.is_ok());
} }
#[tokio::test]
async fn test_list_season_history_command() {
let expected_series_id = 1;
let expected_season_number = 1;
let mut mock_network = MockNetworkTrait::new();
mock_network
.expect_handle_network_event()
.with(eq::<NetworkEvent>(
SonarrEvent::GetSeasonHistory(Some((expected_series_id, expected_season_number))).into(),
))
.times(1)
.returning(|_| {
Ok(Serdeable::Sonarr(SonarrSerdeable::Value(
json!({"testResponse": "response"}),
)))
});
let app_arc = Arc::new(Mutex::new(App::default()));
let list_season_history_command = SonarrListCommand::SeasonHistory {
series_id: 1,
season_number: 1,
};
let result =
SonarrListCommandHandler::with(&app_arc, list_season_history_command, &mut mock_network)
.handle()
.await;
assert!(result.is_ok());
}
} }
} }
+3 -3
View File
@@ -1895,7 +1895,7 @@ impl<'a, 'b> Network<'a, 'b> {
async fn get_sonarr_season_history( async fn get_sonarr_season_history(
&mut self, &mut self,
series_season_id_tuple: Option<(i64, i64)>, series_season_id_tuple: Option<(i64, i64)>,
) -> Result<SonarrHistoryWrapper> { ) -> Result<Vec<SonarrHistoryItem>> {
let event = SonarrEvent::GetSeasonHistory(None); let event = SonarrEvent::GetSeasonHistory(None);
let (series_id, season_number) = let (series_id, season_number) =
if let Some((series_id, season_number)) = series_season_id_tuple { if let Some((series_id, season_number)) = series_season_id_tuple {
@@ -1915,12 +1915,12 @@ impl<'a, 'b> Network<'a, 'b> {
.await; .await;
self self
.handle_request::<(), SonarrHistoryWrapper>(request_props, |history_response, mut app| { .handle_request::<(), Vec<SonarrHistoryItem>>(request_props, |history_items, mut app| {
if app.data.sonarr_data.season_details_modal.is_none() { if app.data.sonarr_data.season_details_modal.is_none() {
app.data.sonarr_data.season_details_modal = Some(SeasonDetailsModal::default()); app.data.sonarr_data.season_details_modal = Some(SeasonDetailsModal::default());
} }
let mut history_vec = history_response.records; let mut history_vec = history_items;
history_vec.sort_by(|a, b| a.id.cmp(&b.id)); history_vec.sort_by(|a, b| a.id.cmp(&b.id));
app app
.data .data
+12 -12
View File
@@ -4168,7 +4168,7 @@ mod test {
#[tokio::test] #[tokio::test]
async fn test_handle_get_sonarr_season_history_event() { async fn test_handle_get_sonarr_season_history_event() {
let history_json = json!({"records": [{ let history_json = json!([{
"id": 123, "id": 123,
"sourceTitle": "z episode", "sourceTitle": "z episode",
"episodeId": 1007, "episodeId": 1007,
@@ -4193,8 +4193,8 @@ mod test {
"droppedPath": "/nfs/nzbget/completed/series/Coolness/something.cool.mkv", "droppedPath": "/nfs/nzbget/completed/series/Coolness/something.cool.mkv",
"importedPath": "/nfs/tv/Coolness/Season 1/Coolness - S01E01 - Something Cool Bluray-1080p.mkv" "importedPath": "/nfs/tv/Coolness/Season 1/Coolness - S01E01 - Something Cool Bluray-1080p.mkv"
} }
}]}); }]);
let response: SonarrHistoryWrapper = serde_json::from_value(history_json.clone()).unwrap(); let response: Vec<SonarrHistoryItem> = serde_json::from_value(history_json.clone()).unwrap();
let expected_history_items = vec![ let expected_history_items = vec![
SonarrHistoryItem { SonarrHistoryItem {
id: 123, id: 123,
@@ -4247,7 +4247,7 @@ mod test {
.sort_asc = true; .sort_asc = true;
let mut network = Network::new(&app_arc, CancellationToken::new(), Client::new()); let mut network = Network::new(&app_arc, CancellationToken::new(), Client::new());
if let SonarrSerdeable::SonarrHistoryWrapper(history) = network if let SonarrSerdeable::SonarrHistoryItems(history) = network
.handle_sonarr_event(SonarrEvent::GetSeasonHistory(None)) .handle_sonarr_event(SonarrEvent::GetSeasonHistory(None))
.await .await
.unwrap() .unwrap()
@@ -4284,7 +4284,7 @@ mod test {
#[tokio::test] #[tokio::test]
async fn test_handle_get_sonarr_season_history_event_uses_provided_series_id_and_season_number() { async fn test_handle_get_sonarr_season_history_event_uses_provided_series_id_and_season_number() {
let history_json = json!({"records": [{ let history_json = json!([{
"id": 123, "id": 123,
"sourceTitle": "z episode", "sourceTitle": "z episode",
"episodeId": 1007, "episodeId": 1007,
@@ -4309,8 +4309,8 @@ mod test {
"droppedPath": "/nfs/nzbget/completed/series/Coolness/something.cool.mkv", "droppedPath": "/nfs/nzbget/completed/series/Coolness/something.cool.mkv",
"importedPath": "/nfs/tv/Coolness/Season 1/Coolness - S01E01 - Something Cool Bluray-1080p.mkv" "importedPath": "/nfs/tv/Coolness/Season 1/Coolness - S01E01 - Something Cool Bluray-1080p.mkv"
} }
}]}); }]);
let response: SonarrHistoryWrapper = serde_json::from_value(history_json.clone()).unwrap(); let response: Vec<SonarrHistoryItem> = serde_json::from_value(history_json.clone()).unwrap();
let expected_history_items = vec![ let expected_history_items = vec![
SonarrHistoryItem { SonarrHistoryItem {
id: 123, id: 123,
@@ -4363,7 +4363,7 @@ mod test {
.sort_asc = true; .sort_asc = true;
let mut network = Network::new(&app_arc, CancellationToken::new(), Client::new()); let mut network = Network::new(&app_arc, CancellationToken::new(), Client::new());
if let SonarrSerdeable::SonarrHistoryWrapper(history) = network if let SonarrSerdeable::SonarrHistoryItems(history) = network
.handle_sonarr_event(SonarrEvent::GetSeasonHistory(Some((2, 2)))) .handle_sonarr_event(SonarrEvent::GetSeasonHistory(Some((2, 2))))
.await .await
.unwrap() .unwrap()
@@ -4400,7 +4400,7 @@ mod test {
#[tokio::test] #[tokio::test]
async fn test_handle_get_sonarr_season_history_event_empty_season_details_modal() { async fn test_handle_get_sonarr_season_history_event_empty_season_details_modal() {
let history_json = json!({"records": [{ let history_json = json!([{
"id": 123, "id": 123,
"sourceTitle": "z episode", "sourceTitle": "z episode",
"episodeId": 1007, "episodeId": 1007,
@@ -4425,8 +4425,8 @@ mod test {
"droppedPath": "/nfs/nzbget/completed/series/Coolness/something.cool.mkv", "droppedPath": "/nfs/nzbget/completed/series/Coolness/something.cool.mkv",
"importedPath": "/nfs/tv/Coolness/Season 1/Coolness - S01E01 - Something Cool Bluray-1080p.mkv" "importedPath": "/nfs/tv/Coolness/Season 1/Coolness - S01E01 - Something Cool Bluray-1080p.mkv"
} }
}]}); }]);
let response: SonarrHistoryWrapper = serde_json::from_value(history_json.clone()).unwrap(); let response: Vec<SonarrHistoryItem> = serde_json::from_value(history_json.clone()).unwrap();
let expected_history_items = vec![ let expected_history_items = vec![
SonarrHistoryItem { SonarrHistoryItem {
id: 123, id: 123,
@@ -4467,7 +4467,7 @@ mod test {
.set_items(vec![season()]); .set_items(vec![season()]);
let mut network = Network::new(&app_arc, CancellationToken::new(), Client::new()); let mut network = Network::new(&app_arc, CancellationToken::new(), Client::new());
if let SonarrSerdeable::SonarrHistoryWrapper(history) = network if let SonarrSerdeable::SonarrHistoryItems(history) = network
.handle_sonarr_event(SonarrEvent::GetSeasonHistory(None)) .handle_sonarr_event(SonarrEvent::GetSeasonHistory(None))
.await .await
.unwrap() .unwrap()