feat(readarr): fetch data on tick for the active block

Give dispatch_by_readarr_block a real match so each view requests what it
needs, and refresh the shared metadata on first render and on every poll
tick. Until now the Readarr tab rendered but never fetched anything.

Guard the conditional arms the way the other servarrs do: the manual
author search only fetches when no releases are cached, book history only
when the books table is populated, and the manual book search only when
the book details modal exists with no cached releases.

Add the selection id helpers the arms read from, named to avoid colliding
with the equivalents the other servarrs already define on App.

Book edition details gets no arm on purpose. There is no endpoint to fetch
a single edition, and the popup is fully populated before it opens.
This commit is contained in:
Dark-Alex-17 committed 2026-09-02 16:32:06 -06:00
1 parent dbd84d929d
commit d6deb6eea2
2 files changed
+892 -6

No files matched your search

+202 -2
View File
@@ -1,5 +1,8 @@
use super::App;
use crate::models::servarr_data::readarr::readarr_data::ActiveReadarrBlock;
use crate::{
models::servarr_data::readarr::readarr_data::ActiveReadarrBlock,
network::readarr_network::ReadarrEvent,
};
pub mod readarr_context_clues;
@@ -10,12 +13,179 @@ mod readarr_tests;
impl App<'_> {
pub(super) async fn dispatch_by_readarr_block(
&mut self,
_active_readarr_block: &ActiveReadarrBlock,
active_readarr_block: &ActiveReadarrBlock,
) {
match active_readarr_block {
ActiveReadarrBlock::Authors => {
self
.dispatch_network_event(ReadarrEvent::GetQualityProfiles.into())
.await;
self
.dispatch_network_event(ReadarrEvent::GetMetadataProfiles.into())
.await;
self
.dispatch_network_event(ReadarrEvent::GetTags.into())
.await;
self
.dispatch_network_event(ReadarrEvent::ListAuthors.into())
.await;
}
ActiveReadarrBlock::Blocklist => {
self
.dispatch_network_event(ReadarrEvent::GetBlocklist.into())
.await;
}
ActiveReadarrBlock::Downloads => {
self
.dispatch_network_event(ReadarrEvent::GetDownloads(500).into())
.await;
}
ActiveReadarrBlock::AuthorDetails => {
self
.dispatch_network_event(ReadarrEvent::GetBooks(self.extract_author_id().await).into())
.await;
}
ActiveReadarrBlock::AuthorHistory => {
self
.dispatch_network_event(
ReadarrEvent::GetAuthorHistory(self.extract_author_id().await).into(),
)
.await;
}
ActiveReadarrBlock::ManualAuthorSearch => {
if self.data.readarr_data.author_releases.is_empty() {
self
.dispatch_network_event(
ReadarrEvent::GetAuthorReleases(self.extract_author_id().await).into(),
)
.await;
}
}
ActiveReadarrBlock::BookDetails => {
let book_id = self.extract_book_id().await;
self
.dispatch_network_event(ReadarrEvent::GetBookEditions(book_id).into())
.await;
self
.dispatch_network_event(ReadarrEvent::GetBookFiles(book_id).into())
.await;
self
.dispatch_network_event(ReadarrEvent::GetDownloads(500).into())
.await;
}
ActiveReadarrBlock::BookHistory => {
if !self.data.readarr_data.books.is_empty() {
self
.dispatch_network_event(
ReadarrEvent::GetBookHistory(
self.extract_author_id().await,
self.extract_book_id().await,
)
.into(),
)
.await;
}
}
ActiveReadarrBlock::ManualBookSearch => {
match self.data.readarr_data.book_details_modal.as_ref() {
Some(book_details_modal) if book_details_modal.book_releases.is_empty() => {
self
.dispatch_network_event(
ReadarrEvent::GetBookReleases(self.extract_book_id().await).into(),
)
.await;
}
_ => (),
}
}
ActiveReadarrBlock::AddAuthorSearchResults => {
self
.dispatch_network_event(
ReadarrEvent::SearchNewAuthor(self.extract_add_new_author_search_query().await).into(),
)
.await;
}
ActiveReadarrBlock::History => {
self
.dispatch_network_event(ReadarrEvent::GetHistory(500).into())
.await
}
ActiveReadarrBlock::RootFolders => {
self
.dispatch_network_event(ReadarrEvent::GetRootFolders.into())
.await;
}
ActiveReadarrBlock::Indexers => {
self
.dispatch_network_event(ReadarrEvent::GetTags.into())
.await;
self
.dispatch_network_event(ReadarrEvent::GetIndexers.into())
.await;
}
ActiveReadarrBlock::AllIndexerSettingsPrompt => {
self
.dispatch_network_event(ReadarrEvent::GetAllIndexerSettings.into())
.await;
}
ActiveReadarrBlock::TestIndexer => {
self
.dispatch_network_event(
ReadarrEvent::TestIndexer(self.extract_readarr_indexer_id().await).into(),
)
.await;
}
ActiveReadarrBlock::TestAllIndexers => {
self
.dispatch_network_event(ReadarrEvent::TestAllIndexers.into())
.await;
}
ActiveReadarrBlock::System => {
self
.dispatch_network_event(ReadarrEvent::GetTasks.into())
.await;
self
.dispatch_network_event(ReadarrEvent::GetQueuedEvents.into())
.await;
self
.dispatch_network_event(ReadarrEvent::GetLogs(500).into())
.await;
}
ActiveReadarrBlock::SystemUpdates => {
self
.dispatch_network_event(ReadarrEvent::GetUpdates.into())
.await;
}
_ => (),
}
self.check_for_readarr_prompt_action().await;
self.reset_tick_count();
}
async fn extract_add_new_author_search_query(&self) -> String {
self
.data
.readarr_data
.add_author_search
.as_ref()
.expect("Add author search is empty")
.text
.clone()
}
async fn extract_author_id(&self) -> i64 {
self.data.readarr_data.authors.current_selection().id
}
async fn extract_book_id(&self) -> i64 {
self.data.readarr_data.books.current_selection().id
}
async fn extract_readarr_indexer_id(&self) -> i64 {
self.data.readarr_data.indexers.current_selection().id
}
async fn check_for_readarr_prompt_action(&mut self) {
if self.data.readarr_data.prompt_confirm {
self.data.readarr_data.prompt_confirm = false;
@@ -28,6 +198,7 @@ impl App<'_> {
pub(super) async fn readarr_on_tick(&mut self, active_readarr_block: ActiveReadarrBlock) {
if self.is_first_render {
self.refresh_readarr_metadata().await;
self.dispatch_by_readarr_block(&active_readarr_block).await;
self.is_first_render = false;
return;
@@ -35,6 +206,7 @@ impl App<'_> {
if self.should_refresh {
self.dispatch_by_readarr_block(&active_readarr_block).await;
self.refresh_readarr_metadata().await;
}
if self.is_routing {
@@ -44,5 +216,33 @@ impl App<'_> {
self.dispatch_by_readarr_block(&active_readarr_block).await;
}
}
if self.tick_count.is_multiple_of(self.tick_until_poll) {
self.refresh_readarr_metadata().await;
}
}
async fn refresh_readarr_metadata(&mut self) {
self
.dispatch_network_event(ReadarrEvent::GetQualityProfiles.into())
.await;
self
.dispatch_network_event(ReadarrEvent::GetMetadataProfiles.into())
.await;
self
.dispatch_network_event(ReadarrEvent::GetTags.into())
.await;
self
.dispatch_network_event(ReadarrEvent::GetRootFolders.into())
.await;
self
.dispatch_network_event(ReadarrEvent::GetDownloads(500).into())
.await;
self
.dispatch_network_event(ReadarrEvent::GetDiskSpace.into())
.await;
self
.dispatch_network_event(ReadarrEvent::GetStatus.into())
.await;
}
}
+690 -4
View File
@@ -1,8 +1,506 @@
#[cfg(test)]
mod tests {
use crate::app::App;
use crate::models::readarr_models::{Author, Book, ReadarrRelease};
use crate::models::servarr_data::readarr::modals::BookDetailsModal;
use crate::models::servarr_data::readarr::readarr_data::ActiveReadarrBlock;
use pretty_assertions::assert_eq;
use crate::models::servarr_models::Indexer;
use crate::network::NetworkEvent;
use crate::network::readarr_network::ReadarrEvent;
use pretty_assertions::{assert_eq, assert_str_eq};
use tokio::sync::mpsc;
#[tokio::test]
async fn test_dispatch_by_readarr_block_authors() {
let (tx, mut rx) = mpsc::channel::<NetworkEvent>(500);
let mut app = App::test_default();
app.data.readarr_data.prompt_confirm = true;
app.network_tx = Some(tx);
app
.dispatch_by_readarr_block(&ActiveReadarrBlock::Authors)
.await;
assert!(app.is_loading);
assert_eq!(
rx.recv().await.unwrap(),
ReadarrEvent::GetQualityProfiles.into()
);
assert_eq!(
rx.recv().await.unwrap(),
ReadarrEvent::GetMetadataProfiles.into()
);
assert_eq!(rx.recv().await.unwrap(), ReadarrEvent::GetTags.into());
assert_eq!(rx.recv().await.unwrap(), ReadarrEvent::ListAuthors.into());
assert!(!app.data.readarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
}
#[tokio::test]
async fn test_dispatch_by_readarr_block_author_details() {
let (tx, mut rx) = mpsc::channel::<NetworkEvent>(500);
let mut app = App::test_default();
app.data.readarr_data.prompt_confirm = true;
app.data.readarr_data.authors.set_items(vec![Author {
id: 1,
..Author::default()
}]);
app.network_tx = Some(tx);
app
.dispatch_by_readarr_block(&ActiveReadarrBlock::AuthorDetails)
.await;
assert!(app.is_loading);
assert_eq!(rx.recv().await.unwrap(), ReadarrEvent::GetBooks(1).into());
assert!(!app.data.readarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
}
#[tokio::test]
async fn test_dispatch_by_blocklist_block() {
let (tx, mut rx) = mpsc::channel::<NetworkEvent>(500);
let mut app = App::test_default();
app.data.readarr_data.prompt_confirm = true;
app.network_tx = Some(tx);
app
.dispatch_by_readarr_block(&ActiveReadarrBlock::Blocklist)
.await;
assert!(app.is_loading);
assert_eq!(rx.recv().await.unwrap(), ReadarrEvent::GetBlocklist.into());
assert!(!app.data.readarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
}
#[tokio::test]
async fn test_dispatch_by_author_history_block() {
let (tx, mut rx) = mpsc::channel::<NetworkEvent>(500);
let mut app = App::test_default();
app.data.readarr_data.prompt_confirm = true;
app.network_tx = Some(tx);
app.data.readarr_data.authors.set_items(vec![Author {
id: 1,
..Author::default()
}]);
app
.dispatch_by_readarr_block(&ActiveReadarrBlock::AuthorHistory)
.await;
assert!(app.is_loading);
assert_eq!(
rx.recv().await.unwrap(),
ReadarrEvent::GetAuthorHistory(1).into()
);
assert!(!app.data.readarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
}
#[tokio::test]
async fn test_dispatch_by_manual_author_search_block() {
let (tx, mut rx) = mpsc::channel::<NetworkEvent>(500);
let mut app = App::test_default();
app.data.readarr_data.prompt_confirm = true;
app.network_tx = Some(tx);
app.data.readarr_data.authors.set_items(vec![Author {
id: 1,
..Author::default()
}]);
app
.dispatch_by_readarr_block(&ActiveReadarrBlock::ManualAuthorSearch)
.await;
assert!(app.is_loading);
assert_eq!(
rx.recv().await.unwrap(),
ReadarrEvent::GetAuthorReleases(1).into()
);
assert!(!app.data.readarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
}
#[tokio::test]
async fn test_dispatch_by_manual_author_search_block_author_releases_non_empty() {
let mut app = App::test_default();
app
.data
.readarr_data
.author_releases
.set_items(vec![ReadarrRelease::default()]);
app
.dispatch_by_readarr_block(&ActiveReadarrBlock::ManualAuthorSearch)
.await;
assert!(!app.is_loading);
assert!(!app.data.readarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
}
#[tokio::test]
async fn test_dispatch_by_book_details_block() {
let (tx, mut rx) = mpsc::channel::<NetworkEvent>(500);
let mut app = App::test_default();
app.data.readarr_data.prompt_confirm = true;
app.network_tx = Some(tx);
app.data.readarr_data.authors.set_items(vec![Author {
id: 1,
..Author::default()
}]);
app.data.readarr_data.books.set_items(vec![Book {
id: 2,
..Book::default()
}]);
app
.dispatch_by_readarr_block(&ActiveReadarrBlock::BookDetails)
.await;
assert!(app.is_loading);
assert_eq!(
rx.recv().await.unwrap(),
ReadarrEvent::GetBookEditions(2).into()
);
assert_eq!(
rx.recv().await.unwrap(),
ReadarrEvent::GetBookFiles(2).into()
);
assert_eq!(
rx.recv().await.unwrap(),
ReadarrEvent::GetDownloads(500).into()
);
assert!(!app.data.readarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
}
#[tokio::test]
async fn test_dispatch_by_book_history_block() {
let (tx, mut rx) = mpsc::channel::<NetworkEvent>(500);
let mut app = App::test_default();
app.data.readarr_data.prompt_confirm = true;
app.network_tx = Some(tx);
app.data.readarr_data.authors.set_items(vec![Author {
id: 1,
..Author::default()
}]);
app.data.readarr_data.books.set_items(vec![Book {
id: 2,
..Book::default()
}]);
app
.dispatch_by_readarr_block(&ActiveReadarrBlock::BookHistory)
.await;
assert!(app.is_loading);
assert_eq!(
rx.recv().await.unwrap(),
ReadarrEvent::GetBookHistory(1, 2).into()
);
assert!(!app.data.readarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
}
#[tokio::test]
async fn test_dispatch_by_book_history_block_no_op_when_books_table_is_empty() {
let (tx, _) = mpsc::channel::<NetworkEvent>(500);
let mut app = App::test_default();
app.data.readarr_data.prompt_confirm = true;
app.network_tx = Some(tx);
app.data.readarr_data.authors.set_items(vec![Author {
id: 1,
..Author::default()
}]);
app
.dispatch_by_readarr_block(&ActiveReadarrBlock::BookHistory)
.await;
assert!(!app.is_loading);
assert!(!app.data.readarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
}
#[tokio::test]
async fn test_dispatch_by_manual_book_search_block() {
let (tx, mut rx) = mpsc::channel::<NetworkEvent>(500);
let mut app = App::test_default();
app.data.readarr_data.prompt_confirm = true;
app.network_tx = Some(tx);
app.data.readarr_data.authors.set_items(vec![Author {
id: 1,
..Author::default()
}]);
app.data.readarr_data.books.set_items(vec![Book {
id: 2,
..Book::default()
}]);
app.data.readarr_data.book_details_modal = Some(BookDetailsModal::default());
app
.dispatch_by_readarr_block(&ActiveReadarrBlock::ManualBookSearch)
.await;
assert!(app.is_loading);
assert_eq!(
rx.recv().await.unwrap(),
ReadarrEvent::GetBookReleases(2).into()
);
assert!(!app.data.readarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
}
#[tokio::test]
async fn test_dispatch_by_manual_book_search_block_is_loading() {
let mut app = App {
is_loading: true,
..App::test_default()
};
app
.dispatch_by_readarr_block(&ActiveReadarrBlock::ManualBookSearch)
.await;
assert!(app.is_loading);
assert!(!app.data.readarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
}
#[tokio::test]
async fn test_dispatch_by_manual_book_search_block_book_releases_non_empty() {
let mut app = App::test_default();
let mut book_details_modal = BookDetailsModal::default();
book_details_modal
.book_releases
.set_items(vec![ReadarrRelease::default()]);
app.data.readarr_data.book_details_modal = Some(book_details_modal);
app
.dispatch_by_readarr_block(&ActiveReadarrBlock::ManualBookSearch)
.await;
assert!(!app.is_loading);
assert!(!app.data.readarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
}
#[tokio::test]
async fn test_dispatch_by_book_edition_details_block_does_not_dispatch() {
let mut app = App::test_default_fully_populated();
app.data.readarr_data.prompt_confirm = false;
app
.dispatch_by_readarr_block(&ActiveReadarrBlock::BookEditionDetails)
.await;
assert!(!app.is_loading);
assert_eq!(app.tick_count, 0);
}
#[tokio::test]
async fn test_dispatch_by_downloads_block() {
let (tx, mut rx) = mpsc::channel::<NetworkEvent>(500);
let mut app = App::test_default();
app.data.readarr_data.prompt_confirm = true;
app.network_tx = Some(tx);
app
.dispatch_by_readarr_block(&ActiveReadarrBlock::Downloads)
.await;
assert!(app.is_loading);
assert_eq!(
rx.recv().await.unwrap(),
ReadarrEvent::GetDownloads(500).into()
);
assert!(!app.data.readarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
}
#[tokio::test]
async fn test_dispatch_by_readarr_block_add_author_search_results() {
let (tx, mut rx) = mpsc::channel::<NetworkEvent>(500);
let mut app = App::test_default();
app.data.readarr_data.prompt_confirm = true;
app.network_tx = Some(tx);
app.data.readarr_data.add_author_search = Some("test author".into());
app
.dispatch_by_readarr_block(&ActiveReadarrBlock::AddAuthorSearchResults)
.await;
assert!(app.is_loading);
assert_eq!(
rx.recv().await.unwrap(),
ReadarrEvent::SearchNewAuthor("test author".to_owned()).into()
);
assert!(!app.data.readarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
}
#[tokio::test]
async fn test_dispatch_by_history_block() {
let (tx, mut rx) = mpsc::channel::<NetworkEvent>(500);
let mut app = App::test_default();
app.data.readarr_data.prompt_confirm = true;
app.network_tx = Some(tx);
app
.dispatch_by_readarr_block(&ActiveReadarrBlock::History)
.await;
assert!(app.is_loading);
assert_eq!(
rx.recv().await.unwrap(),
ReadarrEvent::GetHistory(500).into()
);
assert!(!app.data.readarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
}
#[tokio::test]
async fn test_dispatch_by_root_folders_block() {
let (tx, mut rx) = mpsc::channel::<NetworkEvent>(500);
let mut app = App::test_default();
app.data.readarr_data.prompt_confirm = true;
app.network_tx = Some(tx);
app
.dispatch_by_readarr_block(&ActiveReadarrBlock::RootFolders)
.await;
assert!(app.is_loading);
assert_eq!(
rx.recv().await.unwrap(),
ReadarrEvent::GetRootFolders.into()
);
assert!(!app.data.readarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
}
#[tokio::test]
async fn test_dispatch_by_indexers_block() {
let (tx, mut rx) = mpsc::channel::<NetworkEvent>(500);
let mut app = App::test_default();
app.data.readarr_data.prompt_confirm = true;
app.network_tx = Some(tx);
app
.dispatch_by_readarr_block(&ActiveReadarrBlock::Indexers)
.await;
assert!(app.is_loading);
assert_eq!(rx.recv().await.unwrap(), ReadarrEvent::GetTags.into());
assert_eq!(rx.recv().await.unwrap(), ReadarrEvent::GetIndexers.into());
assert!(!app.data.readarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
}
#[tokio::test]
async fn test_dispatch_by_all_indexer_settings_block() {
let (tx, mut rx) = mpsc::channel::<NetworkEvent>(500);
let mut app = App::test_default();
app.data.readarr_data.prompt_confirm = true;
app.network_tx = Some(tx);
app
.dispatch_by_readarr_block(&ActiveReadarrBlock::AllIndexerSettingsPrompt)
.await;
assert!(app.is_loading);
assert_eq!(
rx.recv().await.unwrap(),
ReadarrEvent::GetAllIndexerSettings.into()
);
assert!(!app.data.readarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
}
#[tokio::test]
async fn test_dispatch_by_test_indexer_block() {
let (tx, mut rx) = mpsc::channel::<NetworkEvent>(500);
let mut app = App::test_default();
app.data.readarr_data.prompt_confirm = true;
app.network_tx = Some(tx);
app.data.readarr_data.indexers.set_items(vec![Indexer {
id: 3,
..Indexer::default()
}]);
app
.dispatch_by_readarr_block(&ActiveReadarrBlock::TestIndexer)
.await;
assert!(app.is_loading);
assert!(!app.data.readarr_data.prompt_confirm);
assert_eq!(
rx.recv().await.unwrap(),
ReadarrEvent::TestIndexer(3).into()
);
assert_eq!(app.tick_count, 0);
}
#[tokio::test]
async fn test_dispatch_by_test_all_indexers_block() {
let (tx, mut rx) = mpsc::channel::<NetworkEvent>(500);
let mut app = App::test_default();
app.data.readarr_data.prompt_confirm = true;
app.network_tx = Some(tx);
app
.dispatch_by_readarr_block(&ActiveReadarrBlock::TestAllIndexers)
.await;
assert!(app.is_loading);
assert!(!app.data.readarr_data.prompt_confirm);
assert_eq!(
rx.recv().await.unwrap(),
ReadarrEvent::TestAllIndexers.into()
);
assert_eq!(app.tick_count, 0);
}
#[tokio::test]
async fn test_dispatch_by_system_block() {
let (tx, mut rx) = mpsc::channel::<NetworkEvent>(500);
let mut app = App::test_default();
app.data.readarr_data.prompt_confirm = true;
app.network_tx = Some(tx);
app
.dispatch_by_readarr_block(&ActiveReadarrBlock::System)
.await;
assert!(app.is_loading);
assert_eq!(rx.recv().await.unwrap(), ReadarrEvent::GetTasks.into());
assert_eq!(
rx.recv().await.unwrap(),
ReadarrEvent::GetQueuedEvents.into()
);
assert_eq!(rx.recv().await.unwrap(), ReadarrEvent::GetLogs(500).into());
assert!(!app.data.readarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
}
#[tokio::test]
async fn test_dispatch_by_system_updates_block() {
let (tx, mut rx) = mpsc::channel::<NetworkEvent>(500);
let mut app = App::test_default();
app.data.readarr_data.prompt_confirm = true;
app.network_tx = Some(tx);
app
.dispatch_by_readarr_block(&ActiveReadarrBlock::SystemUpdates)
.await;
assert!(app.is_loading);
assert_eq!(rx.recv().await.unwrap(), ReadarrEvent::GetUpdates.into());
assert!(!app.data.readarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
}
#[tokio::test]
async fn test_dispatch_by_readarr_block_resets_the_tick_count() {
@@ -55,21 +553,93 @@ mod tests {
}
#[tokio::test]
async fn test_readarr_on_tick_first_render() {
async fn test_check_for_readarr_prompt_action() {
let (tx, mut rx) = mpsc::channel::<NetworkEvent>(500);
let mut app = App::test_default();
app.data.readarr_data.prompt_confirm = true;
app.network_tx = Some(tx);
app.data.readarr_data.prompt_confirm_action = Some(ReadarrEvent::GetStatus);
app.check_for_readarr_prompt_action().await;
assert!(!app.data.readarr_data.prompt_confirm);
assert_eq!(rx.recv().await.unwrap(), ReadarrEvent::GetStatus.into());
assert!(app.should_refresh);
assert_eq!(app.data.readarr_data.prompt_confirm_action, None);
}
#[tokio::test]
async fn test_readarr_refresh_metadata() {
let (tx, mut rx) = mpsc::channel::<NetworkEvent>(500);
let mut app = App::test_default();
app.data.readarr_data.prompt_confirm = true;
app.network_tx = Some(tx);
app.is_routing = true;
app.refresh_readarr_metadata().await;
assert_eq!(
rx.recv().await.unwrap(),
ReadarrEvent::GetQualityProfiles.into()
);
assert_eq!(
rx.recv().await.unwrap(),
ReadarrEvent::GetMetadataProfiles.into()
);
assert_eq!(rx.recv().await.unwrap(), ReadarrEvent::GetTags.into());
assert_eq!(
rx.recv().await.unwrap(),
ReadarrEvent::GetRootFolders.into()
);
assert_eq!(
rx.recv().await.unwrap(),
ReadarrEvent::GetDownloads(500).into()
);
assert_eq!(rx.recv().await.unwrap(), ReadarrEvent::GetDiskSpace.into());
assert_eq!(rx.recv().await.unwrap(), ReadarrEvent::GetStatus.into());
assert!(app.is_loading);
}
#[tokio::test]
async fn test_readarr_on_tick_first_render() {
let (tx, mut rx) = mpsc::channel::<NetworkEvent>(500);
let mut app = App::test_default();
app.data.readarr_data.prompt_confirm = true;
app.network_tx = Some(tx);
app.is_first_render = true;
app.readarr_on_tick(ActiveReadarrBlock::Downloads).await;
assert_eq!(
rx.recv().await.unwrap(),
ReadarrEvent::GetQualityProfiles.into()
);
assert_eq!(
rx.recv().await.unwrap(),
ReadarrEvent::GetMetadataProfiles.into()
);
assert_eq!(rx.recv().await.unwrap(), ReadarrEvent::GetTags.into());
assert_eq!(
rx.recv().await.unwrap(),
ReadarrEvent::GetRootFolders.into()
);
assert_eq!(
rx.recv().await.unwrap(),
ReadarrEvent::GetDownloads(500).into()
);
assert_eq!(rx.recv().await.unwrap(), ReadarrEvent::GetDiskSpace.into());
assert_eq!(rx.recv().await.unwrap(), ReadarrEvent::GetStatus.into());
assert!(app.is_loading);
assert!(!app.data.readarr_data.prompt_confirm);
assert!(!app.is_first_render);
}
#[tokio::test]
async fn test_readarr_on_tick_routing() {
let (tx, mut rx) = mpsc::channel::<NetworkEvent>(500);
let mut app = App::test_default();
app.data.readarr_data.prompt_confirm = true;
app.network_tx = Some(tx);
app.is_routing = true;
app.should_refresh = true;
app.is_first_render = false;
@@ -77,14 +647,19 @@ mod tests {
app.readarr_on_tick(ActiveReadarrBlock::Downloads).await;
assert_eq!(
rx.recv().await.unwrap(),
ReadarrEvent::GetDownloads(500).into()
);
assert!(!app.data.readarr_data.prompt_confirm);
assert!(!app.cancellation_token.is_cancelled());
}
#[tokio::test]
async fn test_readarr_on_tick_routing_while_long_request_is_running_should_cancel_request() {
let (tx, _) = mpsc::channel::<NetworkEvent>(500);
let mut app = App::test_default();
app.data.readarr_data.prompt_confirm = true;
app.network_tx = Some(tx);
app.is_routing = true;
app.should_refresh = false;
app.is_first_render = false;
@@ -97,16 +672,127 @@ mod tests {
#[tokio::test]
async fn test_readarr_on_tick_should_refresh() {
let (tx, mut rx) = mpsc::channel::<NetworkEvent>(500);
let mut app = App::test_default();
app.data.readarr_data.prompt_confirm = true;
app.network_tx = Some(tx);
app.should_refresh = true;
app.is_first_render = false;
app.tick_count = 1;
app.readarr_on_tick(ActiveReadarrBlock::Downloads).await;
assert_eq!(
rx.recv().await.unwrap(),
ReadarrEvent::GetDownloads(500).into()
);
assert!(app.should_refresh);
assert!(!app.data.readarr_data.prompt_confirm);
assert_eq!(app.tick_count, 0);
}
#[tokio::test]
async fn test_readarr_on_tick_should_refresh_does_not_cancel_prompt_requests() {
let (tx, mut rx) = mpsc::channel::<NetworkEvent>(500);
let mut app = App::test_default();
app.data.readarr_data.prompt_confirm = true;
app.network_tx = Some(tx);
app.is_loading = true;
app.is_routing = true;
app.should_refresh = true;
app.is_first_render = false;
app.tick_count = 1;
app.readarr_on_tick(ActiveReadarrBlock::Downloads).await;
assert_eq!(
rx.recv().await.unwrap(),
ReadarrEvent::GetDownloads(500).into()
);
assert!(app.is_loading);
assert!(app.should_refresh);
assert!(!app.data.readarr_data.prompt_confirm);
assert!(!app.cancellation_token.is_cancelled());
}
#[tokio::test]
async fn test_readarr_on_tick_network_tick_frequency() {
let (tx, mut rx) = mpsc::channel::<NetworkEvent>(500);
let mut app = App::test_default();
app.data.readarr_data.prompt_confirm = true;
app.network_tx = Some(tx);
app.is_first_render = false;
app.tick_count = 2;
app.tick_until_poll = 2;
app.readarr_on_tick(ActiveReadarrBlock::Downloads).await;
assert_eq!(
rx.recv().await.unwrap(),
ReadarrEvent::GetQualityProfiles.into()
);
assert_eq!(
rx.recv().await.unwrap(),
ReadarrEvent::GetMetadataProfiles.into()
);
assert_eq!(rx.recv().await.unwrap(), ReadarrEvent::GetTags.into());
assert_eq!(
rx.recv().await.unwrap(),
ReadarrEvent::GetRootFolders.into()
);
assert_eq!(
rx.recv().await.unwrap(),
ReadarrEvent::GetDownloads(500).into()
);
assert!(app.is_loading);
}
#[tokio::test]
async fn test_extract_add_new_author_search_query() {
let app = App::test_default_fully_populated();
let query = app.extract_add_new_author_search_query().await;
assert_str_eq!(query, "Test Author");
}
#[tokio::test]
#[should_panic(expected = "Add author search is empty")]
async fn test_extract_add_new_author_search_query_panics_when_the_query_is_not_set() {
let app = App::test_default();
app.extract_add_new_author_search_query().await;
}
#[tokio::test]
async fn test_extract_author_id() {
let mut app = App::test_default();
app.data.readarr_data.authors.set_items(vec![Author {
id: 1,
..Author::default()
}]);
assert_eq!(app.extract_author_id().await, 1);
}
#[tokio::test]
async fn test_extract_book_id() {
let mut app = App::test_default();
app.data.readarr_data.books.set_items(vec![Book {
id: 2,
..Book::default()
}]);
assert_eq!(app.extract_book_id().await, 2);
}
#[tokio::test]
async fn test_extract_readarr_indexer_id() {
let mut app = App::test_default();
app.data.readarr_data.indexers.set_items(vec![Indexer {
id: 3,
..Indexer::default()
}]);
assert_eq!(app.extract_readarr_indexer_id().await, 3);
}
}