Compare commits
3
Commits
9128e68d0a
...
a5b95c165a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a5b95c165a
|
||
|
|
8ec4ec018b
|
||
|
|
642a5c0a87
|
No files matched your search
@@ -171,6 +171,7 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, ReadarrAddCommand> for ReadarrAddCommandH
|
|||||||
quality_profile_id,
|
quality_profile_id,
|
||||||
metadata_profile_id,
|
metadata_profile_id,
|
||||||
tags,
|
tags,
|
||||||
|
images: Vec::new(),
|
||||||
tag_input_string: None,
|
tag_input_string: None,
|
||||||
add_options: AddAuthorOptions {
|
add_options: AddAuthorOptions {
|
||||||
monitor,
|
monitor,
|
||||||
|
|||||||
@@ -447,6 +447,7 @@ mod tests {
|
|||||||
quality_profile_id: 1,
|
quality_profile_id: 1,
|
||||||
metadata_profile_id: 1,
|
metadata_profile_id: 1,
|
||||||
tags: vec![1, 2],
|
tags: vec![1, 2],
|
||||||
|
images: Vec::new(),
|
||||||
tag_input_string: None,
|
tag_input_string: None,
|
||||||
add_options: AddAuthorOptions {
|
add_options: AddAuthorOptions {
|
||||||
monitor: MonitorType::All,
|
monitor: MonitorType::All,
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ impl AddAuthorHandler<'_, '_> {
|
|||||||
quality_profile_id,
|
quality_profile_id,
|
||||||
metadata_profile_id,
|
metadata_profile_id,
|
||||||
tags: Vec::new(),
|
tags: Vec::new(),
|
||||||
|
images: Vec::new(),
|
||||||
tag_input_string: Some(tags),
|
tag_input_string: Some(tags),
|
||||||
add_options: AddAuthorOptions {
|
add_options: AddAuthorOptions {
|
||||||
monitor,
|
monitor,
|
||||||
|
|||||||
@@ -1237,6 +1237,7 @@ mod tests {
|
|||||||
quality_profile_id: 2222,
|
quality_profile_id: 2222,
|
||||||
metadata_profile_id: 4444,
|
metadata_profile_id: 4444,
|
||||||
tags: Vec::default(),
|
tags: Vec::default(),
|
||||||
|
images: Vec::new(),
|
||||||
tag_input_string: Some("usenet, testing".to_owned()),
|
tag_input_string: Some("usenet, testing".to_owned()),
|
||||||
add_options: AddAuthorOptions {
|
add_options: AddAuthorOptions {
|
||||||
monitor: MonitorType::Future,
|
monitor: MonitorType::Future,
|
||||||
@@ -1628,6 +1629,7 @@ mod tests {
|
|||||||
quality_profile_id: 2222,
|
quality_profile_id: 2222,
|
||||||
metadata_profile_id: 4444,
|
metadata_profile_id: 4444,
|
||||||
tags: Vec::default(),
|
tags: Vec::default(),
|
||||||
|
images: Vec::new(),
|
||||||
tag_input_string: Some("usenet, testing".to_owned()),
|
tag_input_string: Some("usenet, testing".to_owned()),
|
||||||
add_options: AddAuthorOptions {
|
add_options: AddAuthorOptions {
|
||||||
monitor: MonitorType::Future,
|
monitor: MonitorType::Future,
|
||||||
@@ -1795,6 +1797,7 @@ mod tests {
|
|||||||
quality_profile_id: 2222,
|
quality_profile_id: 2222,
|
||||||
metadata_profile_id: 4444,
|
metadata_profile_id: 4444,
|
||||||
tags: Vec::default(),
|
tags: Vec::default(),
|
||||||
|
images: Vec::new(),
|
||||||
tag_input_string: Some("usenet, testing".to_owned()),
|
tag_input_string: Some("usenet, testing".to_owned()),
|
||||||
add_options: AddAuthorOptions {
|
add_options: AddAuthorOptions {
|
||||||
monitor: MonitorType::Future,
|
monitor: MonitorType::Future,
|
||||||
|
|||||||
@@ -222,6 +222,8 @@ pub struct AddAuthorBody {
|
|||||||
pub quality_profile_id: i64,
|
pub quality_profile_id: i64,
|
||||||
pub metadata_profile_id: i64,
|
pub metadata_profile_id: i64,
|
||||||
pub tags: Vec<i64>,
|
pub tags: Vec<i64>,
|
||||||
|
// Readarr-Resurrected persists the add body directly and rejects it when the images list is absent
|
||||||
|
pub images: Vec<Value>,
|
||||||
#[serde(skip_serializing, skip_deserializing)]
|
#[serde(skip_serializing, skip_deserializing)]
|
||||||
pub tag_input_string: Option<String>,
|
pub tag_input_string: Option<String>,
|
||||||
pub add_options: AddAuthorOptions,
|
pub add_options: AddAuthorOptions,
|
||||||
@@ -280,6 +282,7 @@ pub struct Book {
|
|||||||
pub ratings: Option<Ratings>,
|
pub ratings: Option<Ratings>,
|
||||||
pub release_date: Option<DateTime<Utc>>,
|
pub release_date: Option<DateTime<Utc>>,
|
||||||
pub statistics: Option<BookStatistics>,
|
pub statistics: Option<BookStatistics>,
|
||||||
|
#[serde(default)]
|
||||||
pub grabbed: bool,
|
pub grabbed: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -454,6 +457,29 @@ pub struct ReadarrRelease {
|
|||||||
pub quality: QualityWrapper,
|
pub quality: QualityWrapper,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Clone, Debug, PartialEq, Eq)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum ReadarrReleaseResponse {
|
||||||
|
Releases(Vec<ReadarrRelease>),
|
||||||
|
// Chaptarr wraps the release list in a filter summary envelope, even behind its Readarr facade
|
||||||
|
Envelope { releases: Vec<ReadarrRelease> },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ReadarrReleaseResponse {
|
||||||
|
fn default() -> Self {
|
||||||
|
ReadarrReleaseResponse::Releases(Vec::new())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ReadarrReleaseResponse {
|
||||||
|
pub fn into_releases(self) -> Vec<ReadarrRelease> {
|
||||||
|
match self {
|
||||||
|
ReadarrReleaseResponse::Releases(releases)
|
||||||
|
| ReadarrReleaseResponse::Envelope { releases } => releases,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
#[derive(Default, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct BlocklistItem {
|
pub struct BlocklistItem {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ mod tests {
|
|||||||
AddAuthorSearchResult, BlocklistItem, BlocklistResponse, Book, BookFile, DownloadRecord,
|
AddAuthorSearchResult, BlocklistItem, BlocklistResponse, Book, BookFile, DownloadRecord,
|
||||||
DownloadsResponse, Edition, MediaInfo, MonitorType, NewItemMonitorType,
|
DownloadsResponse, Edition, MediaInfo, MonitorType, NewItemMonitorType,
|
||||||
ReadarrHistoryEventType, ReadarrHistoryItem, ReadarrHistoryWrapper, ReadarrRelease,
|
ReadarrHistoryEventType, ReadarrHistoryItem, ReadarrHistoryWrapper, ReadarrRelease,
|
||||||
ReadarrTask, ReadarrTaskName,
|
ReadarrReleaseResponse, ReadarrTask, ReadarrTaskName,
|
||||||
};
|
};
|
||||||
use crate::models::servarr_models::{
|
use crate::models::servarr_models::{
|
||||||
DiskSpace, DownloadStatus, HostConfig, Indexer, IndexerSettings, IndexerTestResult, Log,
|
DiskSpace, DownloadStatus, HostConfig, Indexer, IndexerSettings, IndexerTestResult, Log,
|
||||||
@@ -497,6 +497,29 @@ mod tests {
|
|||||||
assert_eq!(readarr_serdeable, ReadarrSerdeable::Releases(releases));
|
assert_eq!(readarr_serdeable, ReadarrSerdeable::Releases(releases));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_readarr_release_response_default_is_an_empty_release_list() {
|
||||||
|
assert_eq!(
|
||||||
|
ReadarrReleaseResponse::default(),
|
||||||
|
ReadarrReleaseResponse::Releases(Vec::new())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_readarr_release_response_into_releases() {
|
||||||
|
let releases = vec![ReadarrRelease {
|
||||||
|
guid: "test".to_owned(),
|
||||||
|
..ReadarrRelease::default()
|
||||||
|
}];
|
||||||
|
let list = ReadarrReleaseResponse::Releases(releases.clone());
|
||||||
|
let envelope = ReadarrReleaseResponse::Envelope {
|
||||||
|
releases: releases.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(list.into_releases(), releases);
|
||||||
|
assert_eq!(envelope.into_releases(), releases);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_readarr_serdeable_from_security_config() {
|
fn test_readarr_serdeable_from_security_config() {
|
||||||
let security_config = SecurityConfig {
|
let security_config = SecurityConfig {
|
||||||
@@ -939,6 +962,22 @@ mod tests {
|
|||||||
assert_none!(&book.statistics);
|
assert_none!(&book.statistics);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_book_deserialization_defaults_grabbed_when_absent() {
|
||||||
|
let book_json = json!({
|
||||||
|
"id": 1,
|
||||||
|
"title": "The Name of the Wind",
|
||||||
|
"authorId": 3,
|
||||||
|
"foreignBookId": "test-foreign-book-id",
|
||||||
|
"monitored": true
|
||||||
|
});
|
||||||
|
|
||||||
|
let book: Book = serde_json::from_value(book_json).unwrap();
|
||||||
|
|
||||||
|
assert!(!book.grabbed);
|
||||||
|
assert!(book.monitored);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_edition_deserialization() {
|
fn test_edition_deserialization() {
|
||||||
let edition_json = json!({
|
let edition_json = json!({
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use serde_json::{Value, json};
|
|||||||
use crate::models::Route;
|
use crate::models::Route;
|
||||||
use crate::models::readarr_models::{
|
use crate::models::readarr_models::{
|
||||||
AddAuthorBody, AddAuthorSearchResult, Author, DeleteParams, EditAuthorParams, ReadarrCommandBody,
|
AddAuthorBody, AddAuthorSearchResult, Author, DeleteParams, EditAuthorParams, ReadarrCommandBody,
|
||||||
ReadarrHistoryItem, ReadarrRelease,
|
ReadarrHistoryItem, ReadarrRelease, ReadarrReleaseResponse,
|
||||||
};
|
};
|
||||||
use crate::models::servarr_data::readarr::readarr_data::ActiveReadarrBlock;
|
use crate::models::servarr_data::readarr::readarr_data::ActiveReadarrBlock;
|
||||||
use crate::models::stateful_table::StatefulTable;
|
use crate::models::stateful_table::StatefulTable;
|
||||||
@@ -235,10 +235,15 @@ impl Network<'_, '_> {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
self
|
self
|
||||||
.handle_request::<(), Vec<ReadarrRelease>>(request_props, |release_vec, mut app| {
|
.handle_request::<(), ReadarrReleaseResponse>(request_props, |response, mut app| {
|
||||||
app.data.readarr_data.author_releases.set_items(release_vec);
|
app
|
||||||
|
.data
|
||||||
|
.readarr_data
|
||||||
|
.author_releases
|
||||||
|
.set_items(response.into_releases());
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
|
.map(ReadarrReleaseResponse::into_releases)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(in crate::network::readarr_network) async fn get_readarr_author_history(
|
pub(in crate::network::readarr_network) async fn get_readarr_author_history(
|
||||||
|
|||||||
@@ -133,6 +133,48 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_handle_get_author_releases_event_unwraps_the_release_envelope() {
|
||||||
|
let expected_releases = vec![torrent_release()];
|
||||||
|
let releases_json = json!({
|
||||||
|
"releases": [serde_json::from_str::<Value>(RELEASE_JSON).unwrap()],
|
||||||
|
"hiddenReleases": [],
|
||||||
|
"filterSummary": { "totalResults": 1, "displayedCount": 1 },
|
||||||
|
"siblingToggleEnabled": false
|
||||||
|
});
|
||||||
|
let (mock, app, _server) = MockServarrApi::get()
|
||||||
|
.returns(releases_json)
|
||||||
|
.query("authorId=3")
|
||||||
|
.build_for(ReadarrEvent::GetAuthorReleases(3))
|
||||||
|
.await;
|
||||||
|
{
|
||||||
|
let mut app = app.lock().await;
|
||||||
|
app.server_tabs.set_index(3);
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.readarr_data
|
||||||
|
.author_releases
|
||||||
|
.set_items(vec![stale_release()]);
|
||||||
|
}
|
||||||
|
let mut network = test_network(&app);
|
||||||
|
|
||||||
|
let result = network
|
||||||
|
.handle_readarr_event(ReadarrEvent::GetAuthorReleases(3))
|
||||||
|
.await;
|
||||||
|
|
||||||
|
mock.assert_async().await;
|
||||||
|
|
||||||
|
let ReadarrSerdeable::Releases(releases) = result.unwrap() else {
|
||||||
|
panic!("Expected Releases")
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(releases, expected_releases);
|
||||||
|
assert_eq!(
|
||||||
|
app.lock().await.data.readarr_data.author_releases.items,
|
||||||
|
expected_releases
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_handle_get_author_releases_event_failure() {
|
async fn test_handle_get_author_releases_event_failure() {
|
||||||
let stale_releases = vec![stale_release()];
|
let stale_releases = vec![stale_release()];
|
||||||
@@ -638,6 +680,7 @@ mod tests {
|
|||||||
"qualityProfileId": 1,
|
"qualityProfileId": 1,
|
||||||
"metadataProfileId": 1,
|
"metadataProfileId": 1,
|
||||||
"tags": [1, 2],
|
"tags": [1, 2],
|
||||||
|
"images": [],
|
||||||
"addOptions": {
|
"addOptions": {
|
||||||
"monitor": "all",
|
"monitor": "all",
|
||||||
"monitorNewItems": "all",
|
"monitorNewItems": "all",
|
||||||
@@ -680,6 +723,7 @@ mod tests {
|
|||||||
"qualityProfileId": 1,
|
"qualityProfileId": 1,
|
||||||
"metadataProfileId": 1,
|
"metadataProfileId": 1,
|
||||||
"tags": [1, 2],
|
"tags": [1, 2],
|
||||||
|
"images": [],
|
||||||
"addOptions": {
|
"addOptions": {
|
||||||
"monitor": "all",
|
"monitor": "all",
|
||||||
"monitorNewItems": "all",
|
"monitorNewItems": "all",
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use serde_json::{Value, json};
|
|||||||
use crate::models::Route;
|
use crate::models::Route;
|
||||||
use crate::models::readarr_models::{
|
use crate::models::readarr_models::{
|
||||||
Book, BookFile, DeleteParams, Edition, ReadarrCommandBody, ReadarrHistoryItem, ReadarrRelease,
|
Book, BookFile, DeleteParams, Edition, ReadarrCommandBody, ReadarrHistoryItem, ReadarrRelease,
|
||||||
|
ReadarrReleaseResponse,
|
||||||
};
|
};
|
||||||
use crate::models::servarr_data::readarr::readarr_data::ActiveReadarrBlock;
|
use crate::models::servarr_data::readarr::readarr_data::ActiveReadarrBlock;
|
||||||
use crate::network::readarr_network::ReadarrEvent;
|
use crate::network::readarr_network::ReadarrEvent;
|
||||||
@@ -170,16 +171,19 @@ impl Network<'_, '_> {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
self
|
self
|
||||||
.handle_request::<(), Vec<ReadarrRelease>>(request_props, |release_vec, mut app| {
|
.handle_request::<(), ReadarrReleaseResponse>(request_props, |response, mut app| {
|
||||||
let book_details_modal = app
|
let book_details_modal = app
|
||||||
.data
|
.data
|
||||||
.readarr_data
|
.readarr_data
|
||||||
.book_details_modal
|
.book_details_modal
|
||||||
.get_or_insert_default();
|
.get_or_insert_default();
|
||||||
|
|
||||||
book_details_modal.book_releases.set_items(release_vec);
|
book_details_modal
|
||||||
|
.book_releases
|
||||||
|
.set_items(response.into_releases());
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
|
.map(ReadarrReleaseResponse::into_releases)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(in crate::network::readarr_network) async fn get_books(
|
pub(in crate::network::readarr_network) async fn get_books(
|
||||||
|
|||||||
@@ -532,6 +532,57 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_handle_get_book_releases_event_unwraps_the_release_envelope() {
|
||||||
|
let expected_releases = vec![torrent_release()];
|
||||||
|
let releases_json = json!({
|
||||||
|
"releases": [serde_json::from_str::<Value>(RELEASE_JSON).unwrap()],
|
||||||
|
"hiddenReleases": [],
|
||||||
|
"filterSummary": { "totalResults": 1, "displayedCount": 1 },
|
||||||
|
"siblingToggleEnabled": false
|
||||||
|
});
|
||||||
|
let (mock, app, _server) = MockServarrApi::get()
|
||||||
|
.returns(releases_json)
|
||||||
|
.query("bookId=7")
|
||||||
|
.build_for(ReadarrEvent::GetBookReleases(7))
|
||||||
|
.await;
|
||||||
|
{
|
||||||
|
let mut app = app.lock().await;
|
||||||
|
app.server_tabs.set_index(3);
|
||||||
|
let mut book_details_modal = BookDetailsModal::default();
|
||||||
|
book_details_modal
|
||||||
|
.book_releases
|
||||||
|
.set_items(vec![stale_release()]);
|
||||||
|
app.data.readarr_data.book_details_modal = Some(book_details_modal);
|
||||||
|
}
|
||||||
|
let mut network = test_network(&app);
|
||||||
|
|
||||||
|
let result = network
|
||||||
|
.handle_readarr_event(ReadarrEvent::GetBookReleases(7))
|
||||||
|
.await;
|
||||||
|
|
||||||
|
mock.assert_async().await;
|
||||||
|
|
||||||
|
let ReadarrSerdeable::Releases(releases) = result.unwrap() else {
|
||||||
|
panic!("Expected Releases")
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(releases, expected_releases);
|
||||||
|
assert_eq!(
|
||||||
|
app
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.data
|
||||||
|
.readarr_data
|
||||||
|
.book_details_modal
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.book_releases
|
||||||
|
.items,
|
||||||
|
expected_releases
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_handle_get_book_releases_event_empty_book_details_modal() {
|
async fn test_handle_get_book_releases_event_empty_book_details_modal() {
|
||||||
let expected_releases = vec![torrent_release()];
|
let expected_releases = vec![torrent_release()];
|
||||||
|
|||||||
@@ -394,6 +394,7 @@ pub mod test_utils {
|
|||||||
quality_profile_id: 1,
|
quality_profile_id: 1,
|
||||||
metadata_profile_id: 1,
|
metadata_profile_id: 1,
|
||||||
tags: Vec::default(),
|
tags: Vec::default(),
|
||||||
|
images: Vec::new(),
|
||||||
tag_input_string: Some("usenet, testing".to_owned()),
|
tag_input_string: Some("usenet, testing".to_owned()),
|
||||||
add_options: AddAuthorOptions {
|
add_options: AddAuthorOptions {
|
||||||
monitor: MonitorType::All,
|
monitor: MonitorType::All,
|
||||||
|
|||||||
Reference in new issue
Block a user