#![allow(clippy::needless_as_bytes)]
use irondrop::cli::Cli;
use irondrop::server::run_server;
use reqwest::StatusCode;
use reqwest::blocking::Client;
use std::fs::File;
use std::io::Write;
use std::net::SocketAddr;
use std::sync::mpsc;
use std::thread::{self, JoinHandle};
use tempfile::{TempDir, tempdir};
struct TestServer {
addr: SocketAddr,
shutdown_tx: mpsc::Sender<()>,
handle: Option<JoinHandle<()>>,
_temp_dir: TempDir,
}
fn setup_test_server() -> TestServer {
let dir = tempdir().unwrap();
let file_path = dir.path().join("monitor_test.txt");
let mut f = File::create(&file_path).unwrap();
write!(f, "This is a monitor test file.").unwrap();
let cli = Cli {
directory: dir.path().to_path_buf(),
listen: Some("127.0.0.1".to_string()),
port: Some(0),
allowed_extensions: Some("*.txt".to_string()),
threads: Some(4),
chunk_size: Some(1024),
verbose: Some(false),
detailed_logging: Some(false),
username: None,
password: None,
enable_upload: Some(false),
max_upload_size: Some(10240),
config_file: None,
log_dir: None,
};
let (shutdown_tx, shutdown_rx) = mpsc::channel();
let (addr_tx, addr_rx) = mpsc::channel();
let handle = thread::spawn(move || {
if let Err(e) = run_server(cli, Some(shutdown_rx), Some(addr_tx)) {
eprintln!("Server thread failed: {e}");
}
});
let addr = addr_rx.recv().unwrap();
TestServer {
addr,
shutdown_tx,
handle: Some(handle),
_temp_dir: dir,
}
}
impl Drop for TestServer {
fn drop(&mut self) {
if let Some(handle) = self.handle.take() {
let _ = self.shutdown_tx.send(());
let _ = handle.join();
}
}
}
fn extract_bytes_served(json: &str) -> u64 {
if let Some(idx) = json.find("\"bytes_served\":") {
let slice = &json[idx + 15..];
let mut digits = String::new();
for ch in slice.chars() {
if ch.is_ascii_digit() {
digits.push(ch);
} else {
break;
}
}
if let Ok(v) = digits.parse::<u64>() {
return v;
}
}
panic!("bytes_served not found in json: {json}");
}
#[test]
fn test_monitor_json_and_bytes_served_accounting() {
let server = setup_test_server();
let client = Client::new();
let res1 = client
.get(format!("http://{}/_irondrop/monitor?json=1", server.addr))
.send()
.unwrap();
assert_eq!(res1.status(), StatusCode::OK);
let body1 = res1.text().unwrap();
let bytes1 = extract_bytes_served(&body1);
let monitor1_len = body1.as_bytes().len() as u64;
assert!(
bytes1 <= monitor1_len * 2,
"unexpectedly large initial bytes_served: {bytes1}"
);
let res_file = client
.get(format!("http://{}/monitor_test.txt", server.addr))
.send()
.unwrap();
assert_eq!(res_file.status(), StatusCode::OK);
let file_body = res_file.text().unwrap();
let file_len = file_body.as_bytes().len() as u64;
let res2 = client
.get(format!("http://{}/_irondrop/monitor?json=1", server.addr))
.send()
.unwrap();
assert_eq!(res2.status(), StatusCode::OK);
let body2 = res2.text().unwrap();
let bytes2 = extract_bytes_served(&body2);
let delta = bytes2.saturating_sub(bytes1);
assert!(
delta >= file_len,
"bytes_served delta {delta} did not include file bytes {file_len}"
);
assert!(
delta < file_len + 4096,
"bytes_served delta {delta} unreasonably larger than file {file_len}"
);
let res3 = client
.get(format!("http://{}/_irondrop/monitor?json=1", server.addr))
.send()
.unwrap();
assert_eq!(res3.status(), StatusCode::OK);
let body3 = res3.text().unwrap();
let bytes3 = extract_bytes_served(&body3);
assert!(bytes3 >= bytes2, "bytes_served should be non-decreasing");
}
#[test]
fn test_monitor_html_served() {
let server = setup_test_server();
let client = Client::new();
let res = client
.get(format!("http://{}/_irondrop/monitor", server.addr))
.send()
.unwrap();
assert_eq!(res.status(), StatusCode::OK);
let body = res.text().unwrap();
assert!(body.contains("<html"));
assert!(body.to_lowercase().contains("monitor"));
}