use std::fs::{self, File};
use std::io::Write;
use std::path::PathBuf;
use std::thread;
use std::time::Duration;
use xtool::tftp::client::Client;
use xtool::tftp::client::config::ClientConfig;
use xtool::tftp::server::{Config, Server};
use serial_test::serial;
fn setup_test_env() -> (PathBuf, PathBuf) {
let _ = env_logger::builder().is_test(true).try_init();
let test_dir = std::env::temp_dir().join(format!("tftp_test_{}", std::process::id()));
let server_dir = test_dir.join("server");
let client_dir = test_dir.join("client");
fs::create_dir_all(&server_dir).unwrap();
fs::create_dir_all(&client_dir).unwrap();
(server_dir, client_dir)
}
fn cleanup_test_env(test_dir: &PathBuf) {
let _ = fs::remove_dir_all(test_dir);
}
fn start_test_server(port: u16, root_dir: PathBuf) -> thread::JoinHandle<()> {
thread::spawn(move || {
let config =
Config::default().merge_cli("127.0.0.1".to_string(), port, root_dir, false, false);
let mut server = Server::new(&config).unwrap();
server.listen();
})
}
#[test]
#[serial]
fn test_file_download() {
let (server_dir, client_dir) = setup_test_env();
let test_dir = server_dir.parent().unwrap().to_path_buf();
let test_content = b"Hello TFTP World!";
let server_file = server_dir.join("test.txt");
let mut file = File::create(&server_file).unwrap();
file.write_all(test_content).unwrap();
drop(file);
let port = 7000;
let _server_handle = start_test_server(port, server_dir.clone());
thread::sleep(Duration::from_millis(500));
let config = ClientConfig::new("127.0.0.1".parse().unwrap(), port)
.with_block_size(512)
.with_timeout(Duration::from_secs(5));
let client = Client::new(config).unwrap();
let local_file = client_dir.join("downloaded.txt");
let result = client.get("test.txt", &local_file);
assert!(result.is_ok(), "Download failed: {:?}", result.err());
let downloaded_content = fs::read(&local_file).unwrap();
assert_eq!(downloaded_content, test_content);
cleanup_test_env(&test_dir);
}
#[test]
#[serial]
fn test_file_upload() {
let (server_dir, client_dir) = setup_test_env();
let test_dir = server_dir.parent().unwrap().to_path_buf();
let test_content = b"Upload Test Content";
let client_file = client_dir.join("upload.txt");
let mut file = File::create(&client_file).unwrap();
file.write_all(test_content).unwrap();
drop(file);
let port = 7001;
let _server_handle = start_test_server(port, server_dir.clone());
thread::sleep(Duration::from_millis(500));
let config = ClientConfig::new("127.0.0.1".parse().unwrap(), port)
.with_block_size(512)
.with_timeout(Duration::from_secs(5));
let client = Client::new(config).unwrap();
let result = client.put(&client_file, "uploaded.txt");
assert!(result.is_ok(), "Upload failed: {:?}", result.err());
thread::sleep(Duration::from_millis(200));
let server_file = server_dir.join("uploaded.txt");
let uploaded_content = fs::read(&server_file).unwrap();
assert_eq!(uploaded_content, test_content);
cleanup_test_env(&test_dir);
}
#[test]
#[serial]
fn test_large_file_transfer() {
let (server_dir, client_dir) = setup_test_env();
let test_dir = server_dir.parent().unwrap().to_path_buf();
let test_content: Vec<u8> = (0..100_000).map(|i| (i % 256) as u8).collect();
let client_file = client_dir.join("large.dat");
let mut file = File::create(&client_file).unwrap();
file.write_all(&test_content).unwrap();
drop(file);
let port = 7002;
let _server_handle = start_test_server(port, server_dir.clone());
thread::sleep(Duration::from_millis(500));
let config = ClientConfig::new("127.0.0.1".parse().unwrap(), port)
.with_block_size(8192)
.with_timeout(Duration::from_secs(10));
let client = Client::new(config).unwrap();
let result = client.put(&client_file, "large.dat");
assert!(result.is_ok(), "Upload failed: {:?}", result.err());
thread::sleep(Duration::from_millis(200));
let downloaded_file = client_dir.join("large_downloaded.dat");
let result = client.get("large.dat", &downloaded_file);
assert!(result.is_ok(), "Download failed: {:?}", result.err());
let downloaded_content = fs::read(&downloaded_file).unwrap();
assert_eq!(downloaded_content.len(), test_content.len());
assert_eq!(downloaded_content, test_content);
cleanup_test_env(&test_dir);
}
#[test]
#[serial]
fn test_multiple_block_sizes() {
let (server_dir, client_dir) = setup_test_env();
let test_dir = server_dir.parent().unwrap().to_path_buf();
let test_content = b"Block Size Test Content";
let server_file = server_dir.join("blocksize.txt");
let mut file = File::create(&server_file).unwrap();
file.write_all(test_content).unwrap();
drop(file);
let port = 7003;
let _server_handle = start_test_server(port, server_dir.clone());
thread::sleep(Duration::from_millis(500));
for block_size in [512, 1024, 4096, 8192] {
let config = ClientConfig::new("127.0.0.1".parse().unwrap(), port)
.with_block_size(block_size)
.with_timeout(Duration::from_secs(5));
let client = Client::new(config).unwrap();
let local_file = client_dir.join(format!("blocksize_{}.txt", block_size));
let result = client.get("blocksize.txt", &local_file);
assert!(
result.is_ok(),
"Download with block size {} failed: {:?}",
block_size,
result.err()
);
let downloaded_content = fs::read(&local_file).unwrap();
assert_eq!(downloaded_content, test_content);
}
cleanup_test_env(&test_dir);
}
#[test]
#[serial]
fn test_nonexistent_file() {
let (server_dir, client_dir) = setup_test_env();
let test_dir = server_dir.parent().unwrap().to_path_buf();
let port = 7004;
let _server_handle = start_test_server(port, server_dir.clone());
thread::sleep(Duration::from_millis(500));
let config = ClientConfig::new("127.0.0.1".parse().unwrap(), port)
.with_block_size(512)
.with_timeout(Duration::from_secs(5));
let client = Client::new(config).unwrap();
let local_file = client_dir.join("nonexistent.txt");
let result = client.get("nonexistent.txt", &local_file);
assert!(
result.is_err(),
"Should fail when downloading non-existent file"
);
cleanup_test_env(&test_dir);
}