feat: Full CLI and TUI support for the Lidarr Indexers tab

This commit is contained in:
2026-01-14 13:30:51 -07:00
parent 8abcf44866
commit c74d5936d2
91 changed files with 9481 additions and 166 deletions
@@ -0,0 +1,901 @@
#[cfg(test)]
mod tests {
use crate::models::HorizontallyScrollableText;
use crate::models::lidarr_models::LidarrSerdeable;
use crate::models::servarr_data::modals::IndexerTestResultModalItem;
use crate::models::servarr_models::{EditIndexerParams, Indexer, IndexerTestResult};
use crate::network::NetworkResource;
use crate::network::lidarr_network::LidarrEvent;
use crate::network::lidarr_network::lidarr_network_test_utils::test_utils::{
indexer, indexer_settings,
};
use crate::network::network_tests::test_utils::{MockServarrApi, test_network};
use bimap::BiMap;
use mockito::Matcher;
use pretty_assertions::assert_eq;
use serde_json::json;
#[tokio::test]
async fn test_handle_delete_lidarr_indexer_event() {
let (mock, app, _server) = MockServarrApi::delete()
.path("/1")
.build_for(LidarrEvent::DeleteIndexer(1))
.await;
app
.lock()
.await
.data
.lidarr_data
.indexers
.set_items(vec![indexer()]);
app.lock().await.server_tabs.set_index(2);
let mut network = test_network(&app);
assert!(
network
.handle_lidarr_event(LidarrEvent::DeleteIndexer(1))
.await
.is_ok()
);
mock.assert_async().await;
}
#[tokio::test]
async fn test_handle_edit_all_indexer_settings_event() {
let indexer_settings_json = json!({
"id": 1,
"minimumAge": 1,
"maximumSize": 12345,
"retention": 1,
"rssSyncInterval": 60
});
let (mock, app, _server) = MockServarrApi::put()
.with_request_body(indexer_settings_json)
.build_for(LidarrEvent::EditAllIndexerSettings(indexer_settings()))
.await;
app.lock().await.server_tabs.set_index(2);
let mut network = test_network(&app);
assert_ok!(
network
.handle_lidarr_event(LidarrEvent::EditAllIndexerSettings(indexer_settings()))
.await
);
mock.assert_async().await;
}
#[tokio::test]
async fn test_handle_edit_lidarr_indexer_event() {
let expected_edit_indexer_params = EditIndexerParams {
indexer_id: 1,
name: Some("Test Update".to_owned()),
enable_rss: Some(false),
enable_automatic_search: Some(false),
enable_interactive_search: Some(false),
url: Some("https://localhost:9696/1/".to_owned()),
api_key: Some("test1234".to_owned()),
seed_ratio: Some("1.3".to_owned()),
tag_input_string: Some("usenet, testing".to_owned()),
priority: Some(0),
..EditIndexerParams::default()
};
let indexer_details_json = json!({
"enableRss": true,
"enableAutomaticSearch": true,
"enableInteractiveSearch": true,
"name": "Test Indexer",
"priority": 1,
"fields": [
{
"name": "baseUrl",
"value": "https://test.com",
},
{
"name": "apiKey",
"value": "",
},
{
"name": "seedCriteria.seedRatio",
"value": "1.2",
},
],
"tags": [1],
"id": 1
});
let expected_indexer_edit_body_json = json!({
"enableRss": false,
"enableAutomaticSearch": false,
"enableInteractiveSearch": false,
"name": "Test Update",
"priority": 0,
"fields": [
{
"name": "baseUrl",
"value": "https://localhost:9696/1/",
},
{
"name": "apiKey",
"value": "test1234",
},
{
"name": "seedCriteria.seedRatio",
"value": "1.3",
},
],
"tags": [1, 2],
"id": 1
});
let (mock_details_server, app, mut server) = MockServarrApi::get()
.returns(indexer_details_json)
.path("/1")
.build_for(LidarrEvent::GetIndexers)
.await;
let mock_edit_server = server
.mock(
"PUT",
format!(
"/api/v1{}/1?forceSave=true",
LidarrEvent::EditIndexer(expected_edit_indexer_params.clone()).resource()
)
.as_str(),
)
.with_status(202)
.match_header("X-Api-Key", "test1234")
.match_body(Matcher::Json(expected_indexer_edit_body_json))
.create_async()
.await;
app.lock().await.data.lidarr_data.tags_map =
BiMap::from_iter([(1, "usenet".to_owned()), (2, "testing".to_owned())]);
app.lock().await.server_tabs.set_index(2);
let mut network = test_network(&app);
assert_ok!(
network
.handle_lidarr_event(LidarrEvent::EditIndexer(expected_edit_indexer_params))
.await
);
mock_details_server.assert_async().await;
mock_edit_server.assert_async().await;
}
#[tokio::test]
async fn test_handle_edit_lidarr_indexer_event_does_not_overwrite_tags_vec_if_tag_input_string_is_none()
{
let expected_edit_indexer_params = EditIndexerParams {
indexer_id: 1,
name: Some("Test Update".to_owned()),
enable_rss: Some(false),
enable_automatic_search: Some(false),
enable_interactive_search: Some(false),
url: Some("https://localhost:9696/1/".to_owned()),
api_key: Some("test1234".to_owned()),
seed_ratio: Some("1.3".to_owned()),
tags: Some(vec![1, 2]),
priority: Some(0),
..EditIndexerParams::default()
};
let indexer_details_json = json!({
"enableRss": true,
"enableAutomaticSearch": true,
"enableInteractiveSearch": true,
"name": "Test Indexer",
"priority": 1,
"fields": [
{
"name": "baseUrl",
"value": "https://test.com",
},
{
"name": "apiKey",
"value": "",
},
{
"name": "seedCriteria.seedRatio",
"value": "1.2",
},
],
"tags": [1],
"id": 1
});
let expected_indexer_edit_body_json = json!({
"enableRss": false,
"enableAutomaticSearch": false,
"enableInteractiveSearch": false,
"name": "Test Update",
"priority": 0,
"fields": [
{
"name": "baseUrl",
"value": "https://localhost:9696/1/",
},
{
"name": "apiKey",
"value": "test1234",
},
{
"name": "seedCriteria.seedRatio",
"value": "1.3",
},
],
"tags": [1, 2],
"id": 1
});
let (mock_details_server, app, mut server) = MockServarrApi::get()
.returns(indexer_details_json)
.path("/1")
.build_for(LidarrEvent::GetIndexers)
.await;
let mock_edit_server = server
.mock(
"PUT",
format!(
"/api/v1{}/1?forceSave=true",
LidarrEvent::EditIndexer(expected_edit_indexer_params.clone()).resource()
)
.as_str(),
)
.with_status(202)
.match_header("X-Api-Key", "test1234")
.match_body(Matcher::Json(expected_indexer_edit_body_json))
.create_async()
.await;
app.lock().await.server_tabs.set_index(2);
let mut network = test_network(&app);
assert_ok!(
network
.handle_lidarr_event(LidarrEvent::EditIndexer(expected_edit_indexer_params))
.await
);
mock_details_server.assert_async().await;
mock_edit_server.assert_async().await;
}
#[tokio::test]
async fn test_handle_edit_lidarr_indexer_event_does_not_add_seed_ratio_when_seed_ratio_field_is_none_in_details()
{
let expected_edit_indexer_params = EditIndexerParams {
indexer_id: 1,
name: Some("Test Update".to_owned()),
enable_rss: Some(false),
enable_automatic_search: Some(false),
enable_interactive_search: Some(false),
url: Some("https://localhost:9696/1/".to_owned()),
api_key: Some("test1234".to_owned()),
seed_ratio: Some("1.3".to_owned()),
tag_input_string: Some("usenet, testing".to_owned()),
priority: Some(0),
..EditIndexerParams::default()
};
let indexer_details_json = json!({
"enableRss": true,
"enableAutomaticSearch": true,
"enableInteractiveSearch": true,
"name": "Test Indexer",
"priority": 1,
"fields": [
{
"name": "baseUrl",
"value": "https://test.com",
},
{
"name": "apiKey",
"value": "",
},
],
"tags": [1],
"id": 1
});
let expected_indexer_edit_body_json = json!({
"enableRss": false,
"enableAutomaticSearch": false,
"enableInteractiveSearch": false,
"name": "Test Update",
"priority": 0,
"fields": [
{
"name": "baseUrl",
"value": "https://localhost:9696/1/",
},
{
"name": "apiKey",
"value": "test1234",
},
],
"tags": [1, 2],
"id": 1
});
let (mock_details_server, app, mut server) = MockServarrApi::get()
.returns(indexer_details_json)
.path("/1")
.build_for(LidarrEvent::GetIndexers)
.await;
let mock_edit_server = server
.mock(
"PUT",
format!(
"/api/v1{}/1?forceSave=true",
LidarrEvent::EditIndexer(expected_edit_indexer_params.clone()).resource()
)
.as_str(),
)
.with_status(202)
.match_header("X-Api-Key", "test1234")
.match_body(Matcher::Json(expected_indexer_edit_body_json))
.create_async()
.await;
app.lock().await.data.lidarr_data.tags_map =
BiMap::from_iter([(1, "usenet".to_owned()), (2, "testing".to_owned())]);
app.lock().await.server_tabs.set_index(2);
let mut network = test_network(&app);
assert_ok!(
network
.handle_lidarr_event(LidarrEvent::EditIndexer(expected_edit_indexer_params))
.await
);
mock_details_server.assert_async().await;
mock_edit_server.assert_async().await;
}
#[tokio::test]
async fn test_handle_edit_lidarr_indexer_event_populates_the_seed_ratio_value_when_seed_ratio_field_is_present_in_details()
{
let expected_edit_indexer_params = EditIndexerParams {
indexer_id: 1,
name: Some("Test Update".to_owned()),
enable_rss: Some(false),
enable_automatic_search: Some(false),
enable_interactive_search: Some(false),
url: Some("https://localhost:9696/1/".to_owned()),
api_key: Some("test1234".to_owned()),
seed_ratio: Some("1.3".to_owned()),
tag_input_string: Some("usenet, testing".to_owned()),
priority: Some(0),
..EditIndexerParams::default()
};
let indexer_details_json = json!({
"enableRss": true,
"enableAutomaticSearch": true,
"enableInteractiveSearch": true,
"name": "Test Indexer",
"priority": 1,
"fields": [
{
"name": "baseUrl",
"value": "https://test.com",
},
{
"name": "apiKey",
"value": "",
},
{
"name": "seedCriteria.seedRatio",
},
],
"tags": [1],
"id": 1
});
let expected_indexer_edit_body_json = json!({
"enableRss": false,
"enableAutomaticSearch": false,
"enableInteractiveSearch": false,
"name": "Test Update",
"priority": 0,
"fields": [
{
"name": "baseUrl",
"value": "https://localhost:9696/1/",
},
{
"name": "apiKey",
"value": "test1234",
},
{
"name": "seedCriteria.seedRatio",
"value": "1.3",
},
],
"tags": [1, 2],
"id": 1
});
let (mock_details_server, app, mut server) = MockServarrApi::get()
.returns(indexer_details_json)
.path("/1")
.build_for(LidarrEvent::GetIndexers)
.await;
let mock_edit_server = server
.mock(
"PUT",
format!(
"/api/v1{}/1?forceSave=true",
LidarrEvent::EditIndexer(expected_edit_indexer_params.clone()).resource()
)
.as_str(),
)
.with_status(202)
.match_header("X-Api-Key", "test1234")
.match_body(Matcher::Json(expected_indexer_edit_body_json))
.create_async()
.await;
app.lock().await.data.lidarr_data.tags_map =
BiMap::from_iter([(1, "usenet".to_owned()), (2, "testing".to_owned())]);
app.lock().await.server_tabs.set_index(2);
let mut network = test_network(&app);
assert_ok!(
network
.handle_lidarr_event(LidarrEvent::EditIndexer(expected_edit_indexer_params))
.await
);
mock_details_server.assert_async().await;
mock_edit_server.assert_async().await;
}
#[tokio::test]
async fn test_handle_edit_lidarr_indexer_event_defaults_to_previous_values() {
let indexer_details_json = json!({
"enableRss": true,
"enableAutomaticSearch": true,
"enableInteractiveSearch": true,
"name": "Test Indexer",
"priority": 1,
"fields": [
{
"name": "baseUrl",
"value": "https://test.com",
},
{
"name": "apiKey",
"value": "",
},
{
"name": "seedCriteria.seedRatio",
"value": "1.2",
},
],
"tags": [1],
"id": 1
});
let edit_indexer_params = EditIndexerParams {
indexer_id: 1,
..EditIndexerParams::default()
};
let (mock_details_server, app, mut server) = MockServarrApi::get()
.returns(indexer_details_json.clone())
.path("/1")
.build_for(LidarrEvent::GetIndexers)
.await;
let mock_edit_server = server
.mock(
"PUT",
format!(
"/api/v1{}/1?forceSave=true",
LidarrEvent::EditIndexer(edit_indexer_params.clone()).resource()
)
.as_str(),
)
.with_status(202)
.match_header("X-Api-Key", "test1234")
.match_body(Matcher::Json(indexer_details_json))
.create_async()
.await;
app.lock().await.server_tabs.set_index(2);
let mut network = test_network(&app);
assert_ok!(
network
.handle_lidarr_event(LidarrEvent::EditIndexer(edit_indexer_params))
.await
);
mock_details_server.assert_async().await;
mock_edit_server.assert_async().await;
}
#[tokio::test]
async fn test_handle_edit_lidarr_indexer_event_clears_tags_when_clear_tags_is_true() {
let indexer_details_json = json!({
"enableRss": true,
"enableAutomaticSearch": true,
"enableInteractiveSearch": true,
"name": "Test Indexer",
"priority": 1,
"fields": [
{
"name": "baseUrl",
"value": "https://test.com",
},
{
"name": "apiKey",
"value": "",
},
{
"name": "seedCriteria.seedRatio",
"value": "1.2",
},
],
"tags": [1, 2],
"id": 1
});
let expected_edit_indexer_body = json!({
"enableRss": true,
"enableAutomaticSearch": true,
"enableInteractiveSearch": true,
"name": "Test Indexer",
"priority": 1,
"fields": [
{
"name": "baseUrl",
"value": "https://test.com",
},
{
"name": "apiKey",
"value": "",
},
{
"name": "seedCriteria.seedRatio",
"value": "1.2",
},
],
"tags": [],
"id": 1
});
let edit_indexer_params = EditIndexerParams {
indexer_id: 1,
clear_tags: true,
..EditIndexerParams::default()
};
let (async_details_server, app, mut server) = MockServarrApi::get()
.returns(indexer_details_json)
.path("/1")
.build_for(LidarrEvent::GetIndexers)
.await;
let async_edit_server = server
.mock(
"PUT",
format!(
"/api/v1{}/1?forceSave=true",
LidarrEvent::EditIndexer(edit_indexer_params.clone()).resource()
)
.as_str(),
)
.with_status(202)
.match_header("X-Api-Key", "test1234")
.match_body(Matcher::Json(expected_edit_indexer_body))
.create_async()
.await;
app.lock().await.server_tabs.set_index(2);
let mut network = test_network(&app);
assert_ok!(
network
.handle_lidarr_event(LidarrEvent::EditIndexer(edit_indexer_params))
.await
);
async_details_server.assert_async().await;
async_edit_server.assert_async().await;
}
#[tokio::test]
async fn test_handle_get_lidarr_indexers_event() {
let indexers_response_json = json!([{
"enableRss": true,
"enableAutomaticSearch": true,
"enableInteractiveSearch": true,
"supportsRss": true,
"supportsSearch": true,
"protocol": "torrent",
"priority": 25,
"downloadClientId": 0,
"name": "Test Indexer",
"fields": [
{
"name": "baseUrl",
"value": "https://test.com",
},
{
"name": "apiKey",
"value": "",
},
{
"name": "seedCriteria.seedRatio",
"value": "1.2",
},
],
"implementationName": "Torznab",
"implementation": "Torznab",
"configContract": "TorznabSettings",
"tags": [1],
"id": 1
}]);
let response: Vec<Indexer> = serde_json::from_value(indexers_response_json.clone()).unwrap();
let (async_server, app, _server) = MockServarrApi::get()
.returns(indexers_response_json)
.build_for(LidarrEvent::GetIndexers)
.await;
app.lock().await.server_tabs.set_index(2);
let mut network = test_network(&app);
let LidarrSerdeable::Indexers(indexers) = network
.handle_lidarr_event(LidarrEvent::GetIndexers)
.await
.unwrap()
else {
panic!("Expected Indexers")
};
async_server.assert_async().await;
assert_eq!(
app.lock().await.data.lidarr_data.indexers.items,
vec![indexer()]
);
assert_eq!(indexers, response);
}
#[tokio::test]
async fn test_handle_test_lidarr_indexer_event_error() {
let indexer_details_json = json!({
"enableRss": true,
"enableAutomaticSearch": true,
"enableInteractiveSearch": true,
"name": "Test Indexer",
"fields": [
{
"name": "baseUrl",
"value": "https://test.com",
},
{
"name": "apiKey",
"value": "",
},
{
"name": "seedCriteria.seedRatio",
"value": "1.2",
},
],
"tags": [1],
"id": 1
});
let response_json = json!([
{
"isWarning": false,
"propertyName": "",
"errorMessage": "test failure",
"severity": "error"
}]);
let (async_details_server, app, mut server) = MockServarrApi::get()
.returns(indexer_details_json.clone())
.path("/1")
.build_for(LidarrEvent::GetIndexers)
.await;
let async_test_server = server
.mock(
"POST",
format!("/api/v1{}", LidarrEvent::TestIndexer(1).resource()).as_str(),
)
.with_status(400)
.match_header("X-Api-Key", "test1234")
.match_body(Matcher::Json(indexer_details_json.clone()))
.with_body(response_json.to_string())
.create_async()
.await;
app
.lock()
.await
.data
.lidarr_data
.indexers
.set_items(vec![indexer()]);
app.lock().await.server_tabs.set_index(2);
let mut network = test_network(&app);
let LidarrSerdeable::Value(value) = network
.handle_lidarr_event(LidarrEvent::TestIndexer(1))
.await
.unwrap()
else {
panic!("Expected Value")
};
async_details_server.assert_async().await;
async_test_server.assert_async().await;
assert_eq!(
app.lock().await.data.lidarr_data.indexer_test_errors,
Some("\"test failure\"".to_owned())
);
assert_eq!(value, response_json);
}
#[tokio::test]
async fn test_handle_test_lidarr_indexer_event_success() {
let indexer_details_json = json!({
"enableRss": true,
"enableAutomaticSearch": true,
"enableInteractiveSearch": true,
"name": "Test Indexer",
"fields": [
{
"name": "baseUrl",
"value": "https://test.com",
},
{
"name": "apiKey",
"value": "",
},
{
"name": "seedCriteria.seedRatio",
"value": "1.2",
},
],
"tags": [1],
"id": 1
});
let (async_details_server, app, mut server) = MockServarrApi::get()
.returns(indexer_details_json.clone())
.path("/1")
.build_for(LidarrEvent::GetIndexers)
.await;
let async_test_server = server
.mock(
"POST",
format!("/api/v1{}", LidarrEvent::TestIndexer(1).resource()).as_str(),
)
.with_status(200)
.match_header("X-Api-Key", "test1234")
.match_body(Matcher::Json(indexer_details_json.clone()))
.with_body("{}")
.create_async()
.await;
app
.lock()
.await
.data
.lidarr_data
.indexers
.set_items(vec![indexer()]);
app.lock().await.server_tabs.set_index(2);
let mut network = test_network(&app);
let LidarrSerdeable::Value(value) = network
.handle_lidarr_event(LidarrEvent::TestIndexer(1))
.await
.unwrap()
else {
panic!("Expected Value")
};
async_details_server.assert_async().await;
async_test_server.assert_async().await;
assert_eq!(
app.lock().await.data.lidarr_data.indexer_test_errors,
Some(String::new())
);
assert_eq!(value, json!({}));
}
#[tokio::test]
async fn test_handle_test_all_lidarr_indexers_event() {
let indexers = vec![
Indexer {
id: 1,
name: Some("Test 1".to_owned()),
..Indexer::default()
},
Indexer {
id: 2,
name: Some("Test 2".to_owned()),
..Indexer::default()
},
];
let indexer_test_results_modal_items = vec![
IndexerTestResultModalItem {
name: "Test 1".to_owned(),
is_valid: true,
validation_failures: HorizontallyScrollableText::default(),
},
IndexerTestResultModalItem {
name: "Test 2".to_owned(),
is_valid: false,
validation_failures: "Failure for field 'test field 1': test error message, Failure for field 'test field 2': test error message 2".into(),
},
];
let response_json = json!([
{
"id": 1,
"isValid": true,
"validationFailures": []
},
{
"id": 2,
"isValid": false,
"validationFailures": [
{
"propertyName": "test field 1",
"errorMessage": "test error message",
"severity": "error"
},
{
"propertyName": "test field 2",
"errorMessage": "test error message 2",
"severity": "error"
},
]
}]);
let response: Vec<IndexerTestResult> = serde_json::from_value(response_json.clone()).unwrap();
let (async_server, app, _server) = MockServarrApi::post()
.returns(response_json)
.status(400)
.build_for(LidarrEvent::TestAllIndexers)
.await;
app
.lock()
.await
.data
.lidarr_data
.indexers
.set_items(indexers);
app.lock().await.server_tabs.set_index(2);
let mut network = test_network(&app);
let LidarrSerdeable::IndexerTestResults(results) = network
.handle_lidarr_event(LidarrEvent::TestAllIndexers)
.await
.unwrap()
else {
panic!("Expected IndexerTestResults")
};
async_server.assert_async().await;
assert_some!(&app.lock().await.data.lidarr_data.indexer_test_all_results);
assert_eq!(
app
.lock()
.await
.data
.lidarr_data
.indexer_test_all_results
.as_ref()
.unwrap()
.items,
indexer_test_results_modal_items
);
assert_eq!(results, response);
}
#[tokio::test]
async fn test_handle_test_all_lidarr_indexers_event_sets_empty_table_on_api_error() {
let (async_server, app, _server) = MockServarrApi::post()
.status(500)
.build_for(LidarrEvent::TestAllIndexers)
.await;
app.lock().await.server_tabs.set_index(2);
let mut network = test_network(&app);
let result = network
.handle_lidarr_event(LidarrEvent::TestAllIndexers)
.await;
async_server.assert_async().await;
assert_err!(result);
let app = app.lock().await;
assert_some!(&app.data.lidarr_data.indexer_test_all_results);
assert_is_empty!(
app
.data
.lidarr_data
.indexer_test_all_results
.as_ref()
.unwrap()
.items
);
}
}
+419
View File
@@ -0,0 +1,419 @@
use crate::models::servarr_data::modals::IndexerTestResultModalItem;
use crate::models::servarr_models::{
EditIndexerParams, Indexer, IndexerSettings, IndexerTestResult,
};
use crate::models::stateful_table::StatefulTable;
use crate::network::lidarr_network::LidarrEvent;
use crate::network::{Network, RequestMethod};
use anyhow::{Context, Result};
use log::{debug, info};
use serde_json::{Value, json};
#[cfg(test)]
#[path = "lidarr_indexers_network_tests.rs"]
mod lidarr_indexers_network_tests;
impl Network<'_, '_> {
pub(in crate::network::lidarr_network) async fn delete_lidarr_indexer(
&mut self,
indexer_id: i64,
) -> Result<()> {
let event = LidarrEvent::DeleteIndexer(indexer_id);
info!("Deleting Lidarr indexer with id: {indexer_id}");
let request_props = self
.request_props_from(
event,
RequestMethod::Delete,
None::<()>,
Some(format!("/{indexer_id}")),
None,
)
.await;
self
.handle_request::<(), ()>(request_props, |_, _| ())
.await
}
pub(in crate::network::lidarr_network) async fn edit_all_lidarr_indexer_settings(
&mut self,
params: IndexerSettings,
) -> Result<Value> {
info!("Updating Lidarr indexer settings");
let event = LidarrEvent::EditAllIndexerSettings(IndexerSettings::default());
debug!("Indexer settings body: {params:?}");
let request_props = self
.request_props_from(event, RequestMethod::Put, Some(params), None, None)
.await;
self
.handle_request::<IndexerSettings, Value>(request_props, |_, _| {})
.await
}
pub(in crate::network::lidarr_network) async fn get_all_lidarr_indexer_settings(
&mut self,
) -> Result<IndexerSettings> {
info!("Fetching Lidarr indexer settings");
let event = LidarrEvent::GetAllIndexerSettings;
let request_props = self
.request_props_from(event, RequestMethod::Get, None::<()>, None, None)
.await;
self
.handle_request::<(), IndexerSettings>(request_props, |indexer_settings, mut app| {
if app.data.lidarr_data.indexer_settings.is_none() {
app.data.lidarr_data.indexer_settings = Some(indexer_settings);
} else {
debug!("Indexer Settings are being modified. Ignoring update...");
}
})
.await
}
pub(in crate::network::lidarr_network) async fn edit_lidarr_indexer(
&mut self,
mut edit_indexer_params: EditIndexerParams,
) -> Result<()> {
if let Some(tag_input_str) = edit_indexer_params.tag_input_string.as_ref() {
let tag_ids_vec = self.extract_and_add_lidarr_tag_ids_vec(tag_input_str).await;
edit_indexer_params.tags = Some(tag_ids_vec);
}
let detail_event = LidarrEvent::GetIndexers;
let event = LidarrEvent::EditIndexer(EditIndexerParams::default());
let id = edit_indexer_params.indexer_id;
info!("Updating Lidarr indexer with ID: {id}");
info!("Fetching indexer details for indexer with ID: {id}");
let request_props = self
.request_props_from(
detail_event,
RequestMethod::Get,
None::<()>,
Some(format!("/{id}")),
None,
)
.await;
let mut response = String::new();
self
.handle_request::<(), Value>(request_props, |detailed_indexer_body, _| {
response = detailed_indexer_body.to_string()
})
.await?;
info!("Constructing edit indexer body");
let mut detailed_indexer_body: Value = serde_json::from_str(&response)?;
let (
name,
enable_rss,
enable_automatic_search,
enable_interactive_search,
url,
api_key,
seed_ratio,
tags,
priority,
) = {
let priority = detailed_indexer_body["priority"]
.as_i64()
.context("Failed to deserialize indexer 'priority' field")?;
let seed_ratio_field_option = detailed_indexer_body["fields"]
.as_array()
.context("Failed to get indexer 'fields' array")?
.iter()
.find(|field| field["name"] == "seedCriteria.seedRatio");
let name = edit_indexer_params.name.unwrap_or(
detailed_indexer_body["name"]
.as_str()
.context("Failed to deserialize indexer 'name' field")?
.to_owned(),
);
let enable_rss = edit_indexer_params.enable_rss.unwrap_or(
detailed_indexer_body["enableRss"]
.as_bool()
.context("Failed to deserialize indexer 'enableRss' field")?,
);
let enable_automatic_search = edit_indexer_params.enable_automatic_search.unwrap_or(
detailed_indexer_body["enableAutomaticSearch"]
.as_bool()
.context("Failed to deserialize indexer 'enableAutomaticSearch' field")?,
);
let enable_interactive_search = edit_indexer_params.enable_interactive_search.unwrap_or(
detailed_indexer_body["enableInteractiveSearch"]
.as_bool()
.context("Failed to deserialize indexer 'enableInteractiveSearch' field")?,
);
let url = edit_indexer_params.url.unwrap_or(
detailed_indexer_body["fields"]
.as_array()
.context("Failed to get indexer 'fields' array for baseUrl")?
.iter()
.find(|field| field["name"] == "baseUrl")
.context("Field 'baseUrl' was not found in the indexer fields array")?
.get("value")
.unwrap_or(&json!(""))
.as_str()
.context("Failed to deserialize indexer 'baseUrl' value")?
.to_owned(),
);
let api_key = edit_indexer_params.api_key.unwrap_or(
detailed_indexer_body["fields"]
.as_array()
.context("Failed to get indexer 'fields' array for apiKey")?
.iter()
.find(|field| field["name"] == "apiKey")
.context("Field 'apiKey' was not found in the indexer fields array")?
.get("value")
.unwrap_or(&json!(""))
.as_str()
.context("Failed to deserialize indexer 'apiKey' value")?
.to_owned(),
);
let seed_ratio = edit_indexer_params.seed_ratio.unwrap_or_else(|| {
if let Some(seed_ratio_field) = seed_ratio_field_option {
return seed_ratio_field
.get("value")
.unwrap_or(&json!(""))
.as_str()
.unwrap_or("")
.to_owned();
}
String::new()
});
let tags = if edit_indexer_params.clear_tags {
vec![]
} else {
edit_indexer_params.tags.unwrap_or(
detailed_indexer_body["tags"]
.as_array()
.context("Failed to get indexer 'tags' array")?
.iter()
.map(|item| {
item
.as_i64()
.context("Failed to deserialize indexer tag ID")
})
.collect::<Result<Vec<_>>>()?,
)
};
let priority = edit_indexer_params.priority.unwrap_or(priority);
(
name,
enable_rss,
enable_automatic_search,
enable_interactive_search,
url,
api_key,
seed_ratio,
tags,
priority,
)
};
*detailed_indexer_body
.get_mut("name")
.context("Failed to get mutable reference to indexer 'name' field")? = json!(name);
*detailed_indexer_body
.get_mut("priority")
.context("Failed to get mutable reference to indexer 'priority' field")? = json!(priority);
*detailed_indexer_body
.get_mut("enableRss")
.context("Failed to get mutable reference to indexer 'enableRss' field")? = json!(enable_rss);
*detailed_indexer_body
.get_mut("enableAutomaticSearch")
.context("Failed to get mutable reference to indexer 'enableAutomaticSearch' field")? =
json!(enable_automatic_search);
*detailed_indexer_body
.get_mut("enableInteractiveSearch")
.context("Failed to get mutable reference to indexer 'enableInteractiveSearch' field")? =
json!(enable_interactive_search);
*detailed_indexer_body
.get_mut("fields")
.and_then(|f| f.as_array_mut())
.context("Failed to get mutable reference to indexer 'fields' array")?
.iter_mut()
.find(|field| field["name"] == "baseUrl")
.context("Failed to find 'baseUrl' field in indexer fields array")?
.get_mut("value")
.context("Failed to get mutable reference to 'baseUrl' value")? = json!(url);
*detailed_indexer_body
.get_mut("fields")
.and_then(|f| f.as_array_mut())
.context("Failed to get mutable reference to indexer 'fields' array for apiKey")?
.iter_mut()
.find(|field| field["name"] == "apiKey")
.context("Failed to find 'apiKey' field in indexer fields array")?
.get_mut("value")
.context("Failed to get mutable reference to 'apiKey' value")? = json!(api_key);
*detailed_indexer_body
.get_mut("tags")
.context("Failed to get mutable reference to indexer 'tags' field")? = json!(tags);
let seed_ratio_field_option = detailed_indexer_body
.get_mut("fields")
.and_then(|f| f.as_array_mut())
.context("Failed to get mutable reference to indexer 'fields' array for seed ratio")?
.iter_mut()
.find(|field| field["name"] == "seedCriteria.seedRatio");
if let Some(seed_ratio_field) = seed_ratio_field_option {
seed_ratio_field
.as_object_mut()
.context("Failed to get mutable reference to 'seedCriteria.seedRatio' object")?
.insert("value".to_string(), json!(seed_ratio));
}
debug!("Edit indexer body: {detailed_indexer_body:?}");
let request_props = self
.request_props_from(
event,
RequestMethod::Put,
Some(detailed_indexer_body),
Some(format!("/{id}")),
Some("forceSave=true".to_owned()),
)
.await;
self
.handle_request::<Value, ()>(request_props, |_, _| ())
.await
}
pub(in crate::network::lidarr_network) async fn get_lidarr_indexers(
&mut self,
) -> Result<Vec<Indexer>> {
info!("Fetching Lidarr indexers");
let event = LidarrEvent::GetIndexers;
let request_props = self
.request_props_from(event, RequestMethod::Get, None::<()>, None, None)
.await;
self
.handle_request::<(), Vec<Indexer>>(request_props, |indexers, mut app| {
app.data.lidarr_data.indexers.set_items(indexers);
})
.await
}
pub(in crate::network::lidarr_network) async fn test_lidarr_indexer(
&mut self,
indexer_id: i64,
) -> Result<Value> {
let detail_event = LidarrEvent::GetIndexers;
let event = LidarrEvent::TestIndexer(indexer_id);
info!("Testing Lidarr indexer with ID: {indexer_id}");
info!("Fetching indexer details for indexer with ID: {indexer_id}");
let request_props = self
.request_props_from(
detail_event,
RequestMethod::Get,
None::<()>,
Some(format!("/{indexer_id}")),
None,
)
.await;
let mut test_body: Value = Value::default();
self
.handle_request::<(), Value>(request_props, |detailed_indexer_body, _| {
test_body = detailed_indexer_body;
})
.await?;
info!("Testing indexer");
let mut request_props = self
.request_props_from(event, RequestMethod::Post, Some(test_body), None, None)
.await;
request_props.ignore_status_code = true;
self
.handle_request::<Value, Value>(request_props, |test_results, mut app| {
if test_results.as_object().is_none() {
let error_message = test_results
.as_array()
.and_then(|arr| arr.first())
.and_then(|item| item.get("errorMessage"))
.map(|msg| msg.to_string())
.unwrap_or_else(|| "Unknown indexer test error".to_string());
app.data.lidarr_data.indexer_test_errors = Some(error_message);
} else {
app.data.lidarr_data.indexer_test_errors = Some(String::new());
};
})
.await
}
pub(in crate::network::lidarr_network) async fn test_all_lidarr_indexers(
&mut self,
) -> Result<Vec<IndexerTestResult>> {
info!("Testing all Lidarr indexers");
let event = LidarrEvent::TestAllIndexers;
let mut request_props = self
.request_props_from(event, RequestMethod::Post, None, None, None)
.await;
request_props.ignore_status_code = true;
let result = self
.handle_request::<(), Vec<IndexerTestResult>>(request_props, |test_results, mut app| {
let mut test_all_indexer_results = StatefulTable::default();
let indexers = app.data.lidarr_data.indexers.items.clone();
let modal_test_results = test_results
.iter()
.map(|result| {
let name = indexers
.iter()
.filter(|&indexer| indexer.id == result.id)
.map(|indexer| indexer.name.clone())
.nth(0)
.unwrap_or_default();
let validation_failures = result
.validation_failures
.iter()
.map(|failure| {
format!(
"Failure for field '{}': {}",
failure.property_name, failure.error_message
)
})
.collect::<Vec<String>>()
.join(", ");
IndexerTestResultModalItem {
name: name.unwrap_or_default(),
is_valid: result.is_valid,
validation_failures: validation_failures.into(),
}
})
.collect();
test_all_indexer_results.set_items(modal_test_results);
app.data.lidarr_data.indexer_test_all_results = Some(test_all_indexer_results);
})
.await;
if result.is_err() {
self
.app
.lock()
.await
.data
.lidarr_data
.indexer_test_all_results = Some(StatefulTable::default());
}
result
}
}
@@ -8,10 +8,13 @@ pub mod test_utils {
LidarrHistoryEventType, LidarrHistoryItem, LidarrHistoryWrapper, Member, MetadataProfile,
NewItemMonitorType, Ratings, SystemStatus,
};
use crate::models::servarr_models::{Quality, QualityProfile, QualityWrapper, RootFolder, Tag};
use crate::models::servarr_models::IndexerSettings;
use crate::models::servarr_models::{
Indexer, IndexerField, Quality, QualityProfile, QualityWrapper, RootFolder, Tag,
};
use bimap::BiMap;
use chrono::DateTime;
use serde_json::Number;
use serde_json::{Number, json};
pub const ADD_ARTIST_SEARCH_RESULT_JSON: &str = r#"{
"foreignArtistId": "test-foreign-id",
@@ -287,4 +290,47 @@ pub mod test_utils {
..LidarrHistoryData::default()
}
}
pub fn indexer() -> Indexer {
Indexer {
enable_rss: true,
enable_automatic_search: true,
enable_interactive_search: true,
supports_rss: true,
supports_search: true,
protocol: "torrent".to_owned(),
priority: 25,
download_client_id: 0,
name: Some("Test Indexer".to_owned()),
implementation_name: Some("Torznab".to_owned()),
implementation: Some("Torznab".to_owned()),
config_contract: Some("TorznabSettings".to_owned()),
tags: vec![Number::from(1)],
id: 1,
fields: Some(vec![
IndexerField {
name: Some("baseUrl".to_owned()),
value: Some(json!("https://test.com")),
},
IndexerField {
name: Some("apiKey".to_owned()),
value: Some(json!("")),
},
IndexerField {
name: Some("seedCriteria.seedRatio".to_owned()),
value: Some(json!("1.2")),
},
]),
}
}
pub fn indexer_settings() -> IndexerSettings {
IndexerSettings {
id: 1,
minimum_age: 1,
retention: 1,
maximum_size: 12345,
rss_sync_interval: 60,
}
}
}
@@ -5,7 +5,7 @@ mod tests {
AddArtistBody, DeleteParams, EditArtistParams, LidarrSerdeable, MetadataProfile,
};
use crate::models::servarr_data::lidarr::modals::EditArtistModal;
use crate::models::servarr_models::{QualityProfile, Tag};
use crate::models::servarr_models::{EditIndexerParams, IndexerSettings, QualityProfile, Tag};
use crate::network::network_tests::test_utils::{MockServarrApi, test_network};
use crate::network::{NetworkEvent, NetworkResource, lidarr_network::LidarrEvent};
use bimap::BiMap;
@@ -15,6 +15,17 @@ mod tests {
use std::sync::Arc;
use tokio::sync::Mutex;
#[rstest]
fn test_resource_all_indexer_settings(
#[values(
LidarrEvent::GetAllIndexerSettings,
LidarrEvent::EditAllIndexerSettings(IndexerSettings::default())
)]
event: LidarrEvent,
) {
assert_str_eq!(event.resource(), "/config/indexer");
}
#[rstest]
fn test_resource_artist(
#[values(
@@ -37,6 +48,18 @@ mod tests {
assert_str_eq!(event.resource(), "/queue");
}
#[rstest]
fn test_resource_indexer(
#[values(
LidarrEvent::GetIndexers,
LidarrEvent::DeleteIndexer(0),
LidarrEvent::EditIndexer(EditIndexerParams::default())
)]
event: LidarrEvent,
) {
assert_str_eq!(event.resource(), "/indexer");
}
#[rstest]
fn test_resource_history(#[values(LidarrEvent::GetHistory(0))] event: LidarrEvent) {
assert_str_eq!(event.resource(), "/history");
@@ -107,6 +130,8 @@ mod tests {
#[case(LidarrEvent::GetTags, "/tag")]
#[case(LidarrEvent::HealthCheck, "/health")]
#[case(LidarrEvent::MarkHistoryItemAsFailed(0), "/history/failed")]
#[case(LidarrEvent::TestIndexer(0), "/indexer/test")]
#[case(LidarrEvent::TestAllIndexers, "/indexer/testall")]
fn test_resource(#[case] event: LidarrEvent, #[case] expected_uri: &str) {
assert_str_eq!(event.resource(), expected_uri);
}
+42 -1
View File
@@ -6,11 +6,12 @@ use crate::models::lidarr_models::{
AddArtistBody, AddLidarrRootFolderBody, DeleteParams, EditArtistParams, LidarrSerdeable,
MetadataProfile,
};
use crate::models::servarr_models::{QualityProfile, Tag};
use crate::models::servarr_models::{EditIndexerParams, IndexerSettings, QualityProfile, Tag};
use crate::network::{Network, RequestMethod};
mod downloads;
mod history;
mod indexers;
mod library;
mod root_folders;
mod system;
@@ -31,16 +32,21 @@ pub enum LidarrEvent {
DeleteAlbum(DeleteParams),
DeleteArtist(DeleteParams),
DeleteDownload(i64),
DeleteIndexer(i64),
DeleteRootFolder(i64),
DeleteTag(i64),
EditArtist(EditArtistParams),
EditAllIndexerSettings(IndexerSettings),
EditIndexer(EditIndexerParams),
GetAlbums(i64),
GetAlbumDetails(i64),
GetAllIndexerSettings,
GetArtistDetails(i64),
GetDiskSpace,
GetDownloads(u64),
GetHistory(u64),
GetHostConfig,
GetIndexers,
MarkHistoryItemAsFailed(i64),
GetMetadataProfiles,
GetQualityProfiles,
@@ -51,6 +57,8 @@ pub enum LidarrEvent {
HealthCheck,
ListArtists,
SearchNewArtist(String),
TestIndexer(i64),
TestAllIndexers,
ToggleAlbumMonitoring(i64),
ToggleArtistMonitoring(i64),
TriggerAutomaticArtistSearch(i64),
@@ -63,6 +71,9 @@ impl NetworkResource for LidarrEvent {
fn resource(&self) -> &'static str {
match &self {
LidarrEvent::AddTag(_) | LidarrEvent::DeleteTag(_) | LidarrEvent::GetTags => "/tag",
LidarrEvent::GetAllIndexerSettings | LidarrEvent::EditAllIndexerSettings(_) => {
"/config/indexer"
}
LidarrEvent::DeleteArtist(_)
| LidarrEvent::EditArtist(_)
| LidarrEvent::GetArtistDetails(_)
@@ -78,6 +89,9 @@ impl NetworkResource for LidarrEvent {
LidarrEvent::GetHistory(_) => "/history",
LidarrEvent::MarkHistoryItemAsFailed(_) => "/history/failed",
LidarrEvent::GetHostConfig | LidarrEvent::GetSecurityConfig => "/config/host",
LidarrEvent::GetIndexers | LidarrEvent::DeleteIndexer(_) | LidarrEvent::EditIndexer(_) => {
"/indexer"
}
LidarrEvent::TriggerAutomaticArtistSearch(_)
| LidarrEvent::UpdateAllArtists
| LidarrEvent::UpdateAndScanArtist(_)
@@ -87,6 +101,8 @@ impl NetworkResource for LidarrEvent {
LidarrEvent::GetRootFolders
| LidarrEvent::AddRootFolder(_)
| LidarrEvent::DeleteRootFolder(_) => "/rootfolder",
LidarrEvent::TestIndexer(_) => "/indexer/test",
LidarrEvent::TestAllIndexers => "/indexer/testall",
LidarrEvent::GetStatus => "/system/status",
LidarrEvent::HealthCheck => "/health",
LidarrEvent::SearchNewArtist(_) => "/artist/lookup",
@@ -121,6 +137,18 @@ impl Network<'_, '_> {
.delete_lidarr_download(download_id)
.await
.map(LidarrSerdeable::from),
LidarrEvent::EditAllIndexerSettings(params) => self
.edit_all_lidarr_indexer_settings(params)
.await
.map(LidarrSerdeable::from),
LidarrEvent::EditIndexer(params) => self
.edit_lidarr_indexer(params)
.await
.map(LidarrSerdeable::from),
LidarrEvent::DeleteIndexer(indexer_id) => self
.delete_lidarr_indexer(indexer_id)
.await
.map(LidarrSerdeable::from),
LidarrEvent::DeleteRootFolder(root_folder_id) => self
.delete_lidarr_root_folder(root_folder_id)
.await
@@ -132,6 +160,10 @@ impl Network<'_, '_> {
LidarrEvent::GetAlbums(artist_id) => {
self.get_albums(artist_id).await.map(LidarrSerdeable::from)
}
LidarrEvent::GetAllIndexerSettings => self
.get_all_lidarr_indexer_settings()
.await
.map(LidarrSerdeable::from),
LidarrEvent::GetArtistDetails(artist_id) => self
.get_artist_details(artist_id)
.await
@@ -145,6 +177,7 @@ impl Network<'_, '_> {
.get_lidarr_downloads(count)
.await
.map(LidarrSerdeable::from),
LidarrEvent::GetIndexers => self.get_lidarr_indexers().await.map(LidarrSerdeable::from),
LidarrEvent::GetHistory(events) => self
.get_lidarr_history(events)
.await
@@ -206,6 +239,14 @@ impl Network<'_, '_> {
.update_lidarr_downloads()
.await
.map(LidarrSerdeable::from),
LidarrEvent::TestAllIndexers => self
.test_all_lidarr_indexers()
.await
.map(LidarrSerdeable::from),
LidarrEvent::TestIndexer(indexer_id) => self
.test_lidarr_indexer(indexer_id)
.await
.map(LidarrSerdeable::from),
}
}
+11 -1
View File
@@ -1,5 +1,5 @@
use crate::models::servarr_data::modals::IndexerTestResultModalItem;
use crate::models::servarr_models::{DiskSpace, QueueEvent};
use crate::models::servarr_models::{DiskSpace, IndexerSettings, QueueEvent};
use chrono::DateTime;
pub fn diskspace() -> DiskSpace {
@@ -9,6 +9,16 @@ pub fn diskspace() -> DiskSpace {
}
}
pub fn indexer_settings() -> IndexerSettings {
IndexerSettings {
id: 1,
minimum_age: 1,
retention: 1,
maximum_size: 12345,
rss_sync_interval: 60,
}
}
pub fn indexer_test_result() -> IndexerTestResultModalItem {
IndexerTestResultModalItem {
name: "DrunkenSlug".to_owned(),
+1 -1
View File
@@ -1,6 +1,6 @@
use crate::models::servarr_data::modals::IndexerTestResultModalItem;
use crate::models::servarr_models::IndexerSettings;
use crate::models::servarr_models::{EditIndexerParams, Indexer, IndexerTestResult};
use crate::models::sonarr_models::IndexerSettings;
use crate::models::stateful_table::StatefulTable;
use crate::network::sonarr_network::SonarrEvent;
use crate::network::{Network, RequestMethod};
@@ -6,10 +6,9 @@ mod tests {
use crate::models::sonarr_models::SonarrSerdeable;
use crate::network::NetworkResource;
use crate::network::network_tests::test_utils::{MockServarrApi, test_network};
use crate::network::servarr_test_utils::indexer_settings;
use crate::network::sonarr_network::SonarrEvent;
use crate::network::sonarr_network::sonarr_network_test_utils::test_utils::{
indexer, indexer_settings,
};
use crate::network::sonarr_network::sonarr_network_test_utils::test_utils::indexer;
use bimap::BiMap;
use mockito::Matcher;
use pretty_assertions::assert_eq;
@@ -31,11 +30,10 @@ mod tests {
app.lock().await.server_tabs.next();
let mut network = test_network(&app);
assert!(
assert_ok!(
network
.handle_sonarr_event(SonarrEvent::DeleteIndexer(1))
.await
.is_ok()
);
mock.assert_async().await;
@@ -58,11 +56,10 @@ mod tests {
app.lock().await.server_tabs.next();
let mut network = test_network(&app);
assert!(
assert_ok!(
network
.handle_sonarr_event(SonarrEvent::EditAllIndexerSettings(indexer_settings()))
.await
.is_ok()
);
mock.assert_async().await;
@@ -153,11 +150,10 @@ mod tests {
app.lock().await.server_tabs.next();
let mut network = test_network(&app);
assert!(
assert_ok!(
network
.handle_sonarr_event(SonarrEvent::EditIndexer(expected_edit_indexer_params))
.await
.is_ok()
);
mock_details_server.assert_async().await;
@@ -248,11 +244,10 @@ mod tests {
app.lock().await.server_tabs.next();
let mut network = test_network(&app);
assert!(
assert_ok!(
network
.handle_sonarr_event(SonarrEvent::EditIndexer(expected_edit_indexer_params))
.await
.is_ok()
);
mock_details_server.assert_async().await;
@@ -338,11 +333,10 @@ mod tests {
app.lock().await.server_tabs.next();
let mut network = test_network(&app);
assert!(
assert_ok!(
network
.handle_sonarr_event(SonarrEvent::EditIndexer(expected_edit_indexer_params))
.await
.is_ok()
);
mock_details_server.assert_async().await;
@@ -435,11 +429,10 @@ mod tests {
app.lock().await.server_tabs.next();
let mut network = test_network(&app);
assert!(
assert_ok!(
network
.handle_sonarr_event(SonarrEvent::EditIndexer(expected_edit_indexer_params))
.await
.is_ok()
);
mock_details_server.assert_async().await;
@@ -497,11 +490,10 @@ mod tests {
app.lock().await.server_tabs.next();
let mut network = test_network(&app);
assert!(
assert_ok!(
network
.handle_sonarr_event(SonarrEvent::EditIndexer(edit_indexer_params))
.await
.is_ok()
);
mock_details_server.assert_async().await;
@@ -584,11 +576,10 @@ mod tests {
app.lock().await.server_tabs.next();
let mut network = test_network(&app);
assert!(
assert_ok!(
network
.handle_sonarr_event(SonarrEvent::EditIndexer(edit_indexer_params))
.await
.is_ok()
);
async_details_server.assert_async().await;
@@ -642,6 +633,7 @@ mod tests {
else {
panic!("Expected Indexers")
};
async_server.assert_async().await;
assert_eq!(
app.lock().await.data.sonarr_data.indexers.items,
@@ -714,6 +706,7 @@ mod tests {
else {
panic!("Expected Value")
};
async_details_server.assert_async().await;
async_test_server.assert_async().await;
assert_eq!(
@@ -780,6 +773,7 @@ mod tests {
else {
panic!("Expected Value")
};
async_details_server.assert_async().await;
async_test_server.assert_async().await;
assert_eq!(
@@ -860,16 +854,9 @@ mod tests {
else {
panic!("Expected IndexerTestResults")
};
async_server.assert_async().await;
assert!(
app
.lock()
.await
.data
.sonarr_data
.indexer_test_all_results
.is_some()
);
assert_some!(&app.lock().await.data.sonarr_data.indexer_test_all_results);
assert_eq!(
app
.lock()
+5 -3
View File
@@ -5,10 +5,12 @@ use serde_json::{Value, json};
use super::{Network, NetworkEvent, NetworkResource};
use crate::{
models::{
servarr_models::{AddRootFolderBody, EditIndexerParams, Language, QualityProfile, Tag},
servarr_models::{
AddRootFolderBody, EditIndexerParams, IndexerSettings, Language, QualityProfile, Tag,
},
sonarr_models::{
AddSeriesBody, DeleteSeriesParams, EditSeriesParams, IndexerSettings,
SonarrReleaseDownloadBody, SonarrSerdeable, SonarrTaskName,
AddSeriesBody, DeleteSeriesParams, EditSeriesParams, SonarrReleaseDownloadBody,
SonarrSerdeable, SonarrTaskName,
},
},
network::RequestMethod,
@@ -5,10 +5,9 @@ pub mod test_utils {
};
use crate::models::sonarr_models::{
AddSeriesSearchResult, AddSeriesSearchResultStatistics, BlocklistItem, DownloadRecord,
DownloadStatus, DownloadsResponse, Episode, EpisodeFile, IndexerSettings, MediaInfo, Rating,
Season, SeasonStatistics, Series, SeriesStatistics, SeriesStatus, SeriesType,
SonarrHistoryData, SonarrHistoryEventType, SonarrHistoryItem, SonarrRelease, SonarrTask,
SonarrTaskName,
DownloadStatus, 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;
@@ -250,16 +249,6 @@ pub mod test_utils {
}
}
pub fn indexer_settings() -> IndexerSettings {
IndexerSettings {
id: 1,
minimum_age: 1,
retention: 1,
maximum_size: 12345,
rss_sync_interval: 60,
}
}
pub fn language() -> Language {
Language {
id: 1,
@@ -3,11 +3,9 @@ mod test {
use crate::app::App;
use crate::models::servarr_data::sonarr::modals::AddSeriesModal;
use crate::models::servarr_models::{
AddRootFolderBody, EditIndexerParams, Language, QualityProfile, Tag,
};
use crate::models::sonarr_models::{
AddSeriesBody, EditSeriesParams, IndexerSettings, SonarrTaskName,
AddRootFolderBody, EditIndexerParams, IndexerSettings, Language, QualityProfile, Tag,
};
use crate::models::sonarr_models::{AddSeriesBody, EditSeriesParams, SonarrTaskName};
use crate::models::sonarr_models::{DeleteSeriesParams, SonarrSerdeable};
use crate::network::network_tests::test_utils::{MockServarrApi, test_network};
use crate::network::sonarr_network::sonarr_network_test_utils::test_utils::tag;