Check / stable / fmt (push) Failing after 26s
Check / beta / clippy (push) Failing after 1m10s
Check / stable / clippy (push) Failing after 1m10s
Check / nightly / doc (push) Successful in 1m0s
Check / 1.95.0 / check (push) Successful in 1m7s
Check / dockerfile / msrv (push) Successful in 5s
Test Suite / ubuntu / beta (push) Successful in 2m15s
Test Suite / ubuntu / stable (push) Successful in 2m14s
Test Suite / ubuntu / stable / coverage (push) Failing after 1m57s
Test Suite / macos-latest / stable (push) Canceled after 0s
Test Suite / windows-latest / stable (push) Canceled after 0s
316 lines
8.4 KiB
Rust
316 lines
8.4 KiB
Rust
#[cfg(test)]
|
|
mod tests {
|
|
use anyhow::{Error, anyhow};
|
|
use clap::Parser;
|
|
use serde_json::json;
|
|
use std::fs::{self, File};
|
|
use std::io::{BufRead, BufReader, Seek, SeekFrom, Write};
|
|
|
|
use pretty_assertions::{assert_eq, assert_str_eq};
|
|
use tokio::sync::mpsc;
|
|
use tokio_util::sync::CancellationToken;
|
|
|
|
use crate::{
|
|
Cli,
|
|
app::{App, AppConfig, ServarrConfig},
|
|
network::NetworkError,
|
|
utils::{
|
|
build_logging_config, convert_runtime, format_cli_error, format_size, resolve_log_dir,
|
|
select_cli_configuration, was_log_rotated,
|
|
},
|
|
};
|
|
|
|
#[test]
|
|
fn test_select_cli_configuration_selects_valid_named_instance() {
|
|
let selected_config = ServarrConfig {
|
|
name: Some("4K Movies".to_owned()),
|
|
..ServarrConfig::default()
|
|
};
|
|
let config = AppConfig {
|
|
radarr: Some(vec![ServarrConfig::default(), selected_config.clone()]),
|
|
..AppConfig::default()
|
|
};
|
|
let (network_tx, _network_rx) = mpsc::channel(1);
|
|
let mut app = App::new(network_tx, config.clone(), CancellationToken::new());
|
|
let args = Cli::try_parse_from([
|
|
"managarr",
|
|
"radarr",
|
|
"clear-blocklist",
|
|
"--servarr-name",
|
|
"4K Movies",
|
|
])
|
|
.unwrap();
|
|
|
|
select_cli_configuration(
|
|
&mut app,
|
|
&config,
|
|
args.command.as_ref().unwrap(),
|
|
args.global.servarr_name.as_deref(),
|
|
)
|
|
.unwrap();
|
|
|
|
assert_eq!(app.server_tabs.get_active_config(), &Some(selected_config));
|
|
}
|
|
|
|
#[test]
|
|
fn test_format_size() {
|
|
assert_str_eq!(format_size(2_457_600, 2), "2.34 MB");
|
|
assert_str_eq!(format_size(2_469_606_195, 2), "2.30 GB");
|
|
assert_str_eq!(format_size(1_073_741_824, 2), "1.00 GB");
|
|
assert_str_eq!(format_size(1_073_741_823, 2), "1024.00 MB");
|
|
assert_str_eq!(format_size(0, 2), "0.00 MB");
|
|
assert_str_eq!(format_size(6_710_886, 1), "6.4 MB");
|
|
}
|
|
|
|
#[test]
|
|
fn test_convert_runtime() {
|
|
let (hours, minutes) = convert_runtime(154);
|
|
|
|
assert_eq!(hours, 2);
|
|
assert_eq!(minutes, 34);
|
|
}
|
|
|
|
#[test]
|
|
fn test_format_cli_error_network_error_with_json_body() {
|
|
let error = Error::new(NetworkError {
|
|
message: "Request failed. Received 404 Not Found response code with body: Not Found"
|
|
.to_owned(),
|
|
status: 404,
|
|
body: Some(json!({ "status": 404, "title": "Not Found" })),
|
|
});
|
|
|
|
let output = format_cli_error(&error);
|
|
|
|
assert_str_eq!(
|
|
output,
|
|
r#"{
|
|
"error": {
|
|
"message": "Request failed. Received 404 Not Found response code with body: Not Found",
|
|
"status": 404,
|
|
"body": {
|
|
"status": 404,
|
|
"title": "Not Found"
|
|
}
|
|
}
|
|
}"#
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_format_cli_error_network_error_with_string_body() {
|
|
let error = Error::new(NetworkError {
|
|
message:
|
|
"Request failed. Received 503 Service Unavailable response code with body: Service Unavailable"
|
|
.to_owned(),
|
|
status: 503,
|
|
body: Some(json!("Service Unavailable")),
|
|
});
|
|
|
|
let output = format_cli_error(&error);
|
|
|
|
assert_str_eq!(
|
|
output,
|
|
r#"{
|
|
"error": {
|
|
"message": "Request failed. Received 503 Service Unavailable response code with body: Service Unavailable",
|
|
"status": 503,
|
|
"body": "Service Unavailable"
|
|
}
|
|
}"#
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_format_cli_error_network_error_with_null_body() {
|
|
let error = Error::new(NetworkError {
|
|
message: "Request failed. Received 404 Not Found response code with body: ".to_owned(),
|
|
status: 404,
|
|
body: None,
|
|
});
|
|
|
|
let output = format_cli_error(&error);
|
|
|
|
assert_str_eq!(
|
|
output,
|
|
r#"{
|
|
"error": {
|
|
"message": "Request failed. Received 404 Not Found response code with body: ",
|
|
"status": 404,
|
|
"body": null
|
|
}
|
|
}"#
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_format_cli_error_network_error_wrapped_in_context() {
|
|
let error = Error::new(NetworkError {
|
|
message: "Request failed. Received 404 Not Found response code with body: Not Found"
|
|
.to_owned(),
|
|
status: 404,
|
|
body: Some(json!({ "status": 404, "title": "Not Found" })),
|
|
})
|
|
.context("failed to fetch system status");
|
|
|
|
let output = format_cli_error(&error);
|
|
|
|
assert_str_eq!(
|
|
output,
|
|
r#"{
|
|
"error": {
|
|
"message": "Request failed. Received 404 Not Found response code with body: Not Found",
|
|
"status": 404,
|
|
"body": {
|
|
"status": 404,
|
|
"title": "Not Found"
|
|
}
|
|
}
|
|
}"#
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_format_cli_error_plain_anyhow_error() {
|
|
let error = anyhow!("Failed to send request. connection refused ");
|
|
|
|
let output = format_cli_error(&error);
|
|
|
|
assert_str_eq!(
|
|
output,
|
|
r#"{
|
|
"error": {
|
|
"message": "Failed to send request. connection refused ",
|
|
"status": null,
|
|
"body": null
|
|
}
|
|
}"#
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_was_log_rotated_returns_false_when_file_has_not_rotated() {
|
|
let path = std::env::temp_dir().join("managarr_test_no_rotation.log");
|
|
fs::write(&path, "line one\nline two\n").unwrap();
|
|
|
|
let file = File::open(&path).unwrap();
|
|
let mut reader = BufReader::new(file);
|
|
reader.seek(SeekFrom::End(0)).unwrap();
|
|
|
|
assert!(!was_log_rotated(&path, &mut reader));
|
|
|
|
fs::remove_file(&path).unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn test_was_log_rotated_returns_true_and_reopens_reader_after_rotation() {
|
|
let path = std::env::temp_dir().join("managarr_test_rotation.log");
|
|
fs::write(&path, "original content that is long enough\n").unwrap();
|
|
|
|
let file = File::open(&path).unwrap();
|
|
let mut reader = BufReader::new(file);
|
|
reader.seek(SeekFrom::End(0)).unwrap();
|
|
|
|
fs::write(&path, "new\n").unwrap();
|
|
|
|
assert!(was_log_rotated(&path, &mut reader));
|
|
|
|
let mut line = String::new();
|
|
reader.read_line(&mut line).unwrap();
|
|
assert_eq!(line, "new\n");
|
|
|
|
fs::remove_file(&path).unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn test_was_log_rotated_returns_false_when_file_grows() {
|
|
let path = std::env::temp_dir().join("managarr_test_growing.log");
|
|
fs::write(&path, "initial\n").unwrap();
|
|
|
|
let file = File::open(&path).unwrap();
|
|
let mut reader = BufReader::new(file);
|
|
reader.seek(SeekFrom::End(0)).unwrap();
|
|
|
|
let mut appender = fs::OpenOptions::new().append(true).open(&path).unwrap();
|
|
appender.write_all(b"more data\n").unwrap();
|
|
|
|
assert!(!was_log_rotated(&path, &mut reader));
|
|
|
|
fs::remove_file(&path).unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn test_resolve_log_dir_returns_preferred_dir_when_creatable() {
|
|
let preferred = std::env::temp_dir().join(format!(
|
|
"managarr_test_preferred_log_dir_{}",
|
|
std::process::id()
|
|
));
|
|
let _ = fs::remove_dir_all(&preferred);
|
|
|
|
let resolved = resolve_log_dir(preferred.clone());
|
|
|
|
assert_eq!(resolved, preferred);
|
|
assert!(preferred.is_dir());
|
|
|
|
fs::remove_dir_all(&preferred).unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn test_resolve_log_dir_keeps_preferred_dir_and_disables_file_logging_when_uncreatable() {
|
|
let blocker = std::env::temp_dir().join(format!(
|
|
"managarr_test_log_dir_blocker_{}",
|
|
std::process::id()
|
|
));
|
|
let _ = fs::remove_file(&blocker);
|
|
fs::write(&blocker, "not a directory").unwrap();
|
|
let preferred = blocker.join("logs");
|
|
|
|
let resolved = resolve_log_dir(preferred.clone());
|
|
|
|
assert_eq!(resolved, preferred);
|
|
assert!(!resolved.exists());
|
|
|
|
let config = build_logging_config(resolved.join("managarr.log"));
|
|
|
|
assert!(config.appenders().is_empty());
|
|
assert!(config.root().appenders().is_empty());
|
|
|
|
fs::remove_file(&blocker).unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn test_build_logging_config_uses_logfile_appender_when_log_path_is_writable() {
|
|
let log_dir = std::env::temp_dir().join(format!(
|
|
"managarr_test_logging_config_{}",
|
|
std::process::id()
|
|
));
|
|
let _ = fs::remove_dir_all(&log_dir);
|
|
fs::create_dir_all(&log_dir).unwrap();
|
|
|
|
let config = build_logging_config(log_dir.join("managarr.log"));
|
|
|
|
assert_eq!(config.appenders().len(), 1);
|
|
assert_str_eq!(config.appenders()[0].name(), "logfile");
|
|
assert_eq!(config.root().appenders(), ["logfile"]);
|
|
|
|
drop(config);
|
|
fs::remove_dir_all(&log_dir).unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn test_build_logging_config_disables_logging_when_appender_is_unbuildable() {
|
|
let blocker = std::env::temp_dir().join(format!(
|
|
"managarr_test_appender_blocker_{}",
|
|
std::process::id()
|
|
));
|
|
let _ = fs::remove_file(&blocker);
|
|
fs::write(&blocker, "not a directory").unwrap();
|
|
|
|
let config = build_logging_config(blocker.join("logs").join("managarr.log"));
|
|
|
|
assert!(config.appenders().is_empty());
|
|
assert!(config.root().appenders().is_empty());
|
|
|
|
fs::remove_file(&blocker).unwrap();
|
|
}
|
|
}
|