diff --git a/src/models/lidarr_models.rs b/src/models/lidarr_models.rs index 74fe95b1..3d22bab6 100644 --- a/src/models/lidarr_models.rs +++ b/src/models/lidarr_models.rs @@ -27,6 +27,7 @@ pub struct Artist { pub id: i64, pub artist_name: HorizontallyScrollableText, pub foreign_artist_id: String, + #[serde(deserialize_with = "super::from_json_or_default")] pub status: ArtistStatus, pub overview: Option, pub artist_type: Option, @@ -38,6 +39,7 @@ pub struct Artist { #[serde(deserialize_with = "super::from_i64")] pub metadata_profile_id: i64, pub monitored: bool, + #[serde(deserialize_with = "super::from_json_or_default")] pub monitor_new_items: NewItemMonitorType, pub genres: Vec, pub tags: Vec, @@ -161,6 +163,7 @@ pub enum MonitorType { #[serde(rename_all = "camelCase")] pub struct DownloadRecord { pub title: String, + #[serde(deserialize_with = "super::from_json_or_default")] pub status: DownloadStatus, #[serde(deserialize_with = "super::from_i64")] pub id: i64, @@ -219,6 +222,7 @@ pub struct DownloadsResponse { pub struct AddArtistSearchResult { pub foreign_artist_id: String, pub artist_name: HorizontallyScrollableText, + #[serde(deserialize_with = "super::from_json_or_default")] pub status: ArtistStatus, pub overview: Option, pub artist_type: Option, @@ -414,6 +418,7 @@ pub struct LidarrHistoryItem { #[serde(rename_all = "camelCase")] pub struct LidarrTask { pub name: String, + #[serde(deserialize_with = "super::from_json_or_default")] pub task_name: LidarrTaskName, #[serde(deserialize_with = "super::from_i64")] pub interval: i64, diff --git a/src/models/lidarr_models_tests.rs b/src/models/lidarr_models_tests.rs index 618d6f5f..66a1f083 100644 --- a/src/models/lidarr_models_tests.rs +++ b/src/models/lidarr_models_tests.rs @@ -2,13 +2,14 @@ mod tests { use chrono::Utc; use pretty_assertions::{assert_eq, assert_str_eq}; + use serde::de::DeserializeOwned; use serde_json::{Value, json}; use crate::models::lidarr_models::{ AddArtistSearchResult, Album, AudioTags, BlocklistItem, BlocklistResponse, DownloadRecord, DownloadStatus, DownloadsResponse, LidarrHistoryEventType, LidarrHistoryItem, - LidarrHistoryWrapper, LidarrRelease, LidarrTask, MediaInfo, Member, MonitorType, - NewItemMonitorType, Track, TrackFile, + LidarrHistoryWrapper, LidarrRelease, LidarrTask, LidarrTaskName, MediaInfo, Member, + MonitorType, NewItemMonitorType, Track, TrackFile, }; use crate::models::servarr_models::{ DiskSpace, HostConfig, Indexer, IndexerSettings, IndexerTestResult, Log, LogResponse, @@ -19,6 +20,9 @@ mod tests { Serdeable, lidarr_models::{Artist, ArtistStatistics, ArtistStatus, LidarrSerdeable, Ratings}, }; + use crate::network::lidarr_network::lidarr_network_test_utils::test_utils::{ + ADD_ARTIST_SEARCH_RESULT_JSON, ARTIST_JSON, download_record, task, + }; #[test] fn test_artist_status_default() { @@ -946,6 +950,108 @@ mod tests { assert_none!(&search_result.ratings); } + #[test] + fn test_artist_status_deserialization_falls_back_to_default() { + let expected = Artist { + status: ArtistStatus::default(), + ..serde_json::from_str(ARTIST_JSON).unwrap() + }; + + assert_eq!( + deserialize_with_field::(ARTIST_JSON, "status", json!(2)), + expected + ); + assert_eq!( + deserialize_with_field::(ARTIST_JSON, "status", json!("notAThing")), + expected + ); + } + + #[test] + fn test_artist_monitor_new_items_deserialization_falls_back_to_default() { + let expected = Artist { + monitor_new_items: NewItemMonitorType::default(), + ..serde_json::from_str(ARTIST_JSON).unwrap() + }; + + assert_eq!( + deserialize_with_field::(ARTIST_JSON, "monitorNewItems", json!(2)), + expected + ); + assert_eq!( + deserialize_with_field::(ARTIST_JSON, "monitorNewItems", json!("notAThing")), + expected + ); + } + + #[test] + fn test_add_artist_search_result_status_deserialization_falls_back_to_default() { + let expected = AddArtistSearchResult { + status: ArtistStatus::default(), + ..serde_json::from_str(ADD_ARTIST_SEARCH_RESULT_JSON).unwrap() + }; + + assert_eq!( + deserialize_with_field::( + ADD_ARTIST_SEARCH_RESULT_JSON, + "status", + json!(2) + ), + expected + ); + assert_eq!( + deserialize_with_field::( + ADD_ARTIST_SEARCH_RESULT_JSON, + "status", + json!("notAThing") + ), + expected + ); + } + + #[test] + fn test_download_record_status_deserialization_falls_back_to_default() { + let download_record_json = serde_json::to_string(&download_record()).unwrap(); + let expected = DownloadRecord { + status: DownloadStatus::default(), + ..download_record() + }; + + assert_eq!( + deserialize_with_field::(&download_record_json, "status", json!(2)), + expected + ); + assert_eq!( + deserialize_with_field::(&download_record_json, "status", json!("notAThing")), + expected + ); + } + + #[test] + fn test_lidarr_task_task_name_deserialization_falls_back_to_default() { + let task_json = serde_json::to_string(&task()).unwrap(); + let expected = LidarrTask { + task_name: LidarrTaskName::default(), + ..task() + }; + + assert_eq!( + deserialize_with_field::(&task_json, "taskName", json!(2)), + expected + ); + assert_eq!( + deserialize_with_field::(&task_json, "taskName", json!("notAThing")), + expected + ); + } + + fn deserialize_with_field(json: &str, field: &str, value: Value) -> T { + let mut fixture_json: Value = serde_json::from_str(json).unwrap(); + fixture_json[field] = value; + + serde_json::from_value(fixture_json).unwrap() + } + fn deserialize_history_event_type(event_type: Value) -> LidarrHistoryEventType { let history_item_json = json!({ "id": 1, diff --git a/src/models/radarr_models.rs b/src/models/radarr_models.rs index c71fe71f..69481985 100644 --- a/src/models/radarr_models.rs +++ b/src/models/radarr_models.rs @@ -98,6 +98,7 @@ pub struct Collection { pub search_on_add: bool, pub monitored: bool, pub overview: Option, + #[serde(deserialize_with = "super::from_json_or_default")] pub minimum_availability: MinimumAvailability, #[serde(deserialize_with = "super::from_i64")] pub quality_profile_id: i64, @@ -126,7 +127,10 @@ pub struct Credit { pub character: Option, pub department: Option, pub job: Option, - #[serde(rename(deserialize = "type"))] + #[serde( + rename(deserialize = "type"), + deserialize_with = "super::from_json_or_default" + )] pub credit_type: CreditType, } @@ -316,6 +320,7 @@ pub struct Movie { pub tmdb_id: i64, #[serde(deserialize_with = "super::from_i64")] pub quality_profile_id: i64, + #[serde(deserialize_with = "super::from_json_or_default")] pub minimum_availability: MinimumAvailability, pub certification: Option, pub tags: Vec, @@ -469,6 +474,7 @@ pub struct RadarrHistoryItem { #[serde(rename_all = "camelCase")] pub struct RadarrTask { pub name: String, + #[serde(deserialize_with = "super::from_json_or_default")] pub task_name: RadarrTaskName, #[serde(deserialize_with = "super::from_i64")] pub interval: i64, diff --git a/src/models/radarr_models_tests.rs b/src/models/radarr_models_tests.rs index a114a17d..9a7e2ec2 100644 --- a/src/models/radarr_models_tests.rs +++ b/src/models/radarr_models_tests.rs @@ -1,6 +1,7 @@ #[cfg(test)] mod tests { use pretty_assertions::{assert_eq, assert_str_eq}; + use serde::de::DeserializeOwned; use serde_json::{Value, json}; use crate::models::radarr_models::{ @@ -9,8 +10,8 @@ mod tests { use crate::models::{ Serdeable, radarr_models::{ - AddMovieSearchResult, BlocklistItem, BlocklistResponse, Collection, Credit, DiskSpace, - DownloadRecord, DownloadsResponse, Indexer, IndexerSettings, IndexerTestResult, + AddMovieSearchResult, BlocklistItem, BlocklistResponse, Collection, Credit, CreditType, + DiskSpace, DownloadRecord, DownloadsResponse, Indexer, IndexerSettings, IndexerTestResult, MinimumAvailability, Movie, MovieHistoryItem, MovieMonitor, QualityProfile, RadarrRelease, RadarrSerdeable, RadarrTask, RadarrTaskName, Tag, Update, }, @@ -18,6 +19,9 @@ mod tests { HostConfig, Log, LogResponse, QueueEvent, RootFolder, SecurityConfig, SystemStatus, }, }; + use crate::network::radarr_network::radarr_network_test_utils::test_utils::{ + MOVIE_JSON, collection, crew_credit, task, + }; #[test] fn test_task_name_display() { @@ -614,6 +618,88 @@ mod tests { ); } + #[test] + fn test_movie_minimum_availability_deserialization_falls_back_to_default() { + let expected = Movie { + minimum_availability: MinimumAvailability::default(), + ..serde_json::from_str(MOVIE_JSON).unwrap() + }; + + assert_eq!( + deserialize_with_field::(MOVIE_JSON, "minimumAvailability", json!(2)), + expected + ); + assert_eq!( + deserialize_with_field::(MOVIE_JSON, "minimumAvailability", json!("notAThing")), + expected + ); + } + + #[test] + fn test_collection_minimum_availability_deserialization_falls_back_to_default() { + let collection_json = serde_json::to_string(&collection()).unwrap(); + let expected = Collection { + minimum_availability: MinimumAvailability::default(), + ..collection() + }; + + assert_eq!( + deserialize_with_field::(&collection_json, "minimumAvailability", json!(2)), + expected + ); + assert_eq!( + deserialize_with_field::( + &collection_json, + "minimumAvailability", + json!("notAThing") + ), + expected + ); + } + + #[test] + fn test_credit_credit_type_deserialization_falls_back_to_default() { + let credit_json = serde_json::to_string(&crew_credit()).unwrap(); + let expected = Credit { + credit_type: CreditType::default(), + ..crew_credit() + }; + + assert_eq!( + deserialize_with_field::(&credit_json, "type", json!(2)), + expected + ); + assert_eq!( + deserialize_with_field::(&credit_json, "type", json!("notAThing")), + expected + ); + } + + #[test] + fn test_radarr_task_task_name_deserialization_falls_back_to_default() { + let task_json = serde_json::to_string(&task()).unwrap(); + let expected = RadarrTask { + task_name: RadarrTaskName::default(), + ..task() + }; + + assert_eq!( + deserialize_with_field::(&task_json, "taskName", json!(2)), + expected + ); + assert_eq!( + deserialize_with_field::(&task_json, "taskName", json!("notAThing")), + expected + ); + } + + fn deserialize_with_field(json: &str, field: &str, value: Value) -> T { + let mut fixture_json: Value = serde_json::from_str(json).unwrap(); + fixture_json[field] = value; + + serde_json::from_value(fixture_json).unwrap() + } + fn deserialize_history_event_type(event_type: Value) -> RadarrHistoryEventType { let history_item_json = json!({ "id": 1, diff --git a/src/models/readarr_models.rs b/src/models/readarr_models.rs index 17dc3257..c924b19a 100644 --- a/src/models/readarr_models.rs +++ b/src/models/readarr_models.rs @@ -27,6 +27,7 @@ pub struct Author { pub id: i64, pub author_name: HorizontallyScrollableText, pub foreign_author_id: String, + #[serde(deserialize_with = "super::from_json_or_default")] pub status: AuthorStatus, pub overview: Option, pub author_type: Option, @@ -38,6 +39,7 @@ pub struct Author { #[serde(deserialize_with = "super::from_i64")] pub metadata_profile_id: i64, pub monitored: bool, + #[serde(deserialize_with = "super::from_json_or_default")] pub monitor_new_items: NewItemMonitorType, pub genres: Vec, pub tags: Vec, @@ -152,6 +154,7 @@ pub enum MonitorType { #[serde(rename_all = "camelCase")] pub struct DownloadRecord { pub title: String, + #[serde(deserialize_with = "super::from_json_or_default")] pub status: DownloadStatus, #[serde(deserialize_with = "super::from_i64")] pub id: i64, @@ -210,6 +213,7 @@ pub struct DownloadsResponse { pub struct AddAuthorSearchResult { pub foreign_author_id: String, pub author_name: HorizontallyScrollableText, + #[serde(deserialize_with = "super::from_json_or_default")] pub status: AuthorStatus, #[serde(default)] pub ended: bool, @@ -423,6 +427,7 @@ pub struct ReadarrHistoryItem { #[serde(rename_all = "camelCase")] pub struct ReadarrTask { pub name: String, + #[serde(deserialize_with = "super::from_json_or_default")] pub task_name: ReadarrTaskName, #[serde(deserialize_with = "super::from_i64")] pub interval: i64, diff --git a/src/models/readarr_models_tests.rs b/src/models/readarr_models_tests.rs index 20ffb5b5..867495c4 100644 --- a/src/models/readarr_models_tests.rs +++ b/src/models/readarr_models_tests.rs @@ -2,6 +2,7 @@ mod tests { use chrono::Utc; use pretty_assertions::{assert_eq, assert_str_eq}; + use serde::de::DeserializeOwned; use serde_json::{Value, json}; use crate::models::readarr_models::{ @@ -19,6 +20,9 @@ mod tests { Serdeable, readarr_models::{Author, AuthorStatistics, AuthorStatus, Ratings, ReadarrSerdeable}, }; + use crate::network::readarr_network::readarr_network_test_utils::test_utils::{ + ADD_AUTHOR_SEARCH_RESULT_JSON, AUTHOR_JSON, download_record, + }; #[test] fn test_author_status_default() { @@ -1140,6 +1144,113 @@ mod tests { assert_none!(&search_result.ratings); } + #[test] + fn test_author_status_deserialization_falls_back_to_default() { + let expected = Author { + status: AuthorStatus::default(), + ..serde_json::from_str(AUTHOR_JSON).unwrap() + }; + + assert_eq!( + deserialize_with_field::(AUTHOR_JSON, "status", json!(2)), + expected + ); + assert_eq!( + deserialize_with_field::(AUTHOR_JSON, "status", json!("notAThing")), + expected + ); + } + + #[test] + fn test_author_monitor_new_items_deserialization_falls_back_to_default() { + let expected = Author { + monitor_new_items: NewItemMonitorType::default(), + ..serde_json::from_str(AUTHOR_JSON).unwrap() + }; + + assert_eq!( + deserialize_with_field::(AUTHOR_JSON, "monitorNewItems", json!(2)), + expected + ); + assert_eq!( + deserialize_with_field::(AUTHOR_JSON, "monitorNewItems", json!("notAThing")), + expected + ); + } + + #[test] + fn test_add_author_search_result_status_deserialization_falls_back_to_default() { + let expected = AddAuthorSearchResult { + status: AuthorStatus::default(), + ..serde_json::from_str(ADD_AUTHOR_SEARCH_RESULT_JSON).unwrap() + }; + + assert_eq!( + deserialize_with_field::( + ADD_AUTHOR_SEARCH_RESULT_JSON, + "status", + json!(2) + ), + expected + ); + assert_eq!( + deserialize_with_field::( + ADD_AUTHOR_SEARCH_RESULT_JSON, + "status", + json!("notAThing") + ), + expected + ); + } + + #[test] + fn test_download_record_status_deserialization_falls_back_to_default() { + let download_record_json = serde_json::to_string(&download_record()).unwrap(); + let expected = DownloadRecord { + status: DownloadStatus::default(), + ..download_record() + }; + + assert_eq!( + deserialize_with_field::(&download_record_json, "status", json!(2)), + expected + ); + assert_eq!( + deserialize_with_field::(&download_record_json, "status", json!("notAThing")), + expected + ); + } + + #[test] + fn test_readarr_task_task_name_deserialization_falls_back_to_default() { + let task = ReadarrTask { + name: "Backup".to_owned(), + task_name: ReadarrTaskName::Backup, + ..ReadarrTask::default() + }; + let task_json = serde_json::to_string(&task).unwrap(); + let expected = ReadarrTask { + task_name: ReadarrTaskName::default(), + ..task + }; + + assert_eq!( + deserialize_with_field::(&task_json, "taskName", json!(2)), + expected + ); + assert_eq!( + deserialize_with_field::(&task_json, "taskName", json!("notAThing")), + expected + ); + } + + fn deserialize_with_field(json: &str, field: &str, value: Value) -> T { + let mut fixture_json: Value = serde_json::from_str(json).unwrap(); + fixture_json[field] = value; + + serde_json::from_value(fixture_json).unwrap() + } + fn deserialize_history_event_type(event_type: Value) -> ReadarrHistoryEventType { let history_item_json = json!({ "id": 1, diff --git a/src/models/servarr_models.rs b/src/models/servarr_models.rs index ef9d09a3..1d87f8c2 100644 --- a/src/models/servarr_models.rs +++ b/src/models/servarr_models.rs @@ -315,7 +315,9 @@ pub struct RootFolder { #[derive(Default, Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct SecurityConfig { + #[serde(deserialize_with = "super::from_json_or_default")] pub authentication_method: AuthenticationMethod, + #[serde(default, deserialize_with = "super::from_json_or_default")] #[serde(skip_serializing_if = "Option::is_none")] pub authentication_required: Option, #[serde(skip_serializing_if = "Option::is_none")] @@ -323,6 +325,7 @@ pub struct SecurityConfig { #[serde(skip_serializing_if = "Option::is_none")] pub password: Option, pub api_key: String, + #[serde(deserialize_with = "super::from_json_or_default")] pub certificate_validation: CertificateValidation, } diff --git a/src/models/servarr_models_tests.rs b/src/models/servarr_models_tests.rs index 52dd4f2f..98fd176d 100644 --- a/src/models/servarr_models_tests.rs +++ b/src/models/servarr_models_tests.rs @@ -1,10 +1,12 @@ #[cfg(test)] mod tests { use pretty_assertions::{assert_eq, assert_str_eq}; + use serde::de::DeserializeOwned; + use serde_json::{Value, json}; use crate::models::servarr_models::{ AuthenticationMethod, AuthenticationRequired, CertificateValidation, Indexer, QualityProfile, - Update, UpdateChanges, + SecurityConfig, Update, UpdateChanges, }; #[test] @@ -90,4 +92,118 @@ mod tests { } ); } + + #[test] + fn test_security_config_authentication_method_deserialization_falls_back_to_default() { + let security_config_json = serde_json::to_string(&security_config()).unwrap(); + let expected = SecurityConfig { + authentication_method: AuthenticationMethod::default(), + ..security_config() + }; + + assert_eq!( + deserialize_with_field::( + &security_config_json, + "authenticationMethod", + json!(2) + ), + expected + ); + assert_eq!( + deserialize_with_field::( + &security_config_json, + "authenticationMethod", + json!("notAThing") + ), + expected + ); + } + + #[test] + fn test_security_config_authentication_required_deserialization_falls_back_to_none() { + let security_config_json = serde_json::to_string(&security_config()).unwrap(); + let expected = SecurityConfig { + authentication_required: None, + ..security_config() + }; + + assert_eq!( + deserialize_with_field::( + &security_config_json, + "authenticationRequired", + json!(2) + ), + expected + ); + assert_eq!( + deserialize_with_field::( + &security_config_json, + "authenticationRequired", + json!("notAThing") + ), + expected + ); + } + + #[test] + fn test_security_config_deserializes_when_authentication_required_is_absent() { + let mut security_config_json = serde_json::to_value(security_config()).unwrap(); + security_config_json + .as_object_mut() + .unwrap() + .remove("authenticationRequired"); + let expected = SecurityConfig { + authentication_required: None, + ..security_config() + }; + + let deserialized = serde_json::from_value::(security_config_json); + + assert_ok!(&deserialized); + assert_eq!(deserialized.unwrap(), expected); + } + + #[test] + fn test_security_config_certificate_validation_deserialization_falls_back_to_default() { + let security_config_json = serde_json::to_string(&security_config()).unwrap(); + let expected = SecurityConfig { + certificate_validation: CertificateValidation::default(), + ..security_config() + }; + + assert_eq!( + deserialize_with_field::( + &security_config_json, + "certificateValidation", + json!(2) + ), + expected + ); + assert_eq!( + deserialize_with_field::( + &security_config_json, + "certificateValidation", + json!("notAThing") + ), + expected + ); + } + + fn deserialize_with_field(json: &str, field: &str, value: Value) -> T { + let mut fixture_json: Value = serde_json::from_str(json).unwrap(); + fixture_json[field] = value; + + serde_json::from_value(fixture_json).unwrap() + } + + fn security_config() -> SecurityConfig { + SecurityConfig { + authentication_method: AuthenticationMethod::Forms, + authentication_required: Some(AuthenticationRequired::Enabled), + username: Some("admin".to_owned()), + password: Some("password".to_owned()), + api_key: "test-api-key".to_owned(), + certificate_validation: CertificateValidation::Disabled, + } + } } diff --git a/src/models/sonarr_models.rs b/src/models/sonarr_models.rs index 11ee02d2..aca2c844 100644 --- a/src/models/sonarr_models.rs +++ b/src/models/sonarr_models.rs @@ -112,6 +112,7 @@ pub struct DeleteSeriesParams { #[serde(rename_all = "camelCase")] pub struct DownloadRecord { pub title: String, + #[serde(deserialize_with = "super::from_json_or_default")] pub status: DownloadStatus, #[serde(deserialize_with = "super::from_i64")] pub id: i64, @@ -314,12 +315,14 @@ pub struct Series { #[serde(deserialize_with = "super::from_i64")] pub year: i64, pub monitored: bool, + #[serde(deserialize_with = "super::from_json_or_default")] pub series_type: SeriesType, pub path: String, pub genres: Vec, pub tags: Vec, pub ratings: Rating, pub ended: bool, + #[serde(deserialize_with = "super::from_json_or_default")] pub status: SeriesStatus, pub overview: Option, pub network: Option, @@ -556,6 +559,7 @@ pub struct SonarrReleaseDownloadBody { #[serde(rename_all = "camelCase")] pub struct SonarrTask { pub name: String, + #[serde(deserialize_with = "super::from_json_or_default")] pub task_name: SonarrTaskName, #[serde(deserialize_with = "super::from_i64")] pub interval: i64, diff --git a/src/models/sonarr_models_tests.rs b/src/models/sonarr_models_tests.rs index 8e82559a..286cb714 100644 --- a/src/models/sonarr_models_tests.rs +++ b/src/models/sonarr_models_tests.rs @@ -1,6 +1,7 @@ #[cfg(test)] mod tests { use pretty_assertions::{assert_eq, assert_str_eq}; + use serde::de::DeserializeOwned; use serde_json::{Value, json}; use crate::models::{ @@ -17,6 +18,9 @@ mod tests { SonarrTaskName, }, }; + use crate::network::sonarr_network::sonarr_network_test_utils::test_utils::{ + SERIES_JSON, download_record, task, + }; #[test] fn test_episode_display() { @@ -698,6 +702,83 @@ mod tests { ); } + #[test] + fn test_series_series_type_deserialization_falls_back_to_default() { + let expected = Series { + series_type: SeriesType::default(), + ..serde_json::from_str(SERIES_JSON).unwrap() + }; + + assert_eq!( + deserialize_with_field::(SERIES_JSON, "seriesType", json!(2)), + expected + ); + assert_eq!( + deserialize_with_field::(SERIES_JSON, "seriesType", json!("notAThing")), + expected + ); + } + + #[test] + fn test_series_status_deserialization_falls_back_to_default() { + let expected = Series { + status: SeriesStatus::default(), + ..serde_json::from_str(SERIES_JSON).unwrap() + }; + + assert_eq!( + deserialize_with_field::(SERIES_JSON, "status", json!(2)), + expected + ); + assert_eq!( + deserialize_with_field::(SERIES_JSON, "status", json!("notAThing")), + expected + ); + } + + #[test] + fn test_download_record_status_deserialization_falls_back_to_default() { + let download_record_json = serde_json::to_string(&download_record()).unwrap(); + let expected = DownloadRecord { + status: DownloadStatus::default(), + ..download_record() + }; + + assert_eq!( + deserialize_with_field::(&download_record_json, "status", json!(2)), + expected + ); + assert_eq!( + deserialize_with_field::(&download_record_json, "status", json!("notAThing")), + expected + ); + } + + #[test] + fn test_sonarr_task_task_name_deserialization_falls_back_to_default() { + let task_json = serde_json::to_string(&task()).unwrap(); + let expected = SonarrTask { + task_name: SonarrTaskName::default(), + ..task() + }; + + assert_eq!( + deserialize_with_field::(&task_json, "taskName", json!(2)), + expected + ); + assert_eq!( + deserialize_with_field::(&task_json, "taskName", json!("notAThing")), + expected + ); + } + + fn deserialize_with_field(json: &str, field: &str, value: Value) -> T { + let mut fixture_json: Value = serde_json::from_str(json).unwrap(); + fixture_json[field] = value; + + serde_json::from_value(fixture_json).unwrap() + } + fn deserialize_history_event_type(event_type: Value) -> SonarrHistoryEventType { let history_item_json = json!({ "id": 1,