mod common;
use mini_static::Server;
use hyper::Method;
use std::fs;
use tempfile::TempDir;
#[tokio::test]
async fn content_length_on_200_response() {
let root = TempDir::new().unwrap();
let content = b"hello world";
fs::write(root.path().join("file.txt"), content).unwrap();
let server = Server::new(root.path()).unwrap();
let response = common::get(&server, "/file.txt").await;
assert_eq!(response.status().as_u16(), 200);
let content_length = response
.headers()
.get("Content-Length")
.and_then(|v| v.to_str().ok())
.and_then(|s| s.parse::<u64>().ok());
assert_eq!(
content_length,
Some(content.len() as u64),
"Content-Length should match file size"
);
let body = common::body_bytes(response).await;
assert_eq!(&body[..], content, "body should be the file's actual bytes");
}
#[tokio::test]
async fn content_length_matches_exact_file_size() {
let root = TempDir::new().unwrap();
let sizes = vec![0, 1, 10, 100, 1000, 1024, 4096];
for size in sizes {
let content = vec![0u8; size];
let filename = format!("file_{}.bin", size);
fs::write(root.path().join(&filename), &content).unwrap();
let server = Server::new(root.path()).unwrap();
let response = common::get(&server, &format!("/{}", filename)).await;
assert_eq!(response.status().as_u16(), 200);
let content_length = response
.headers()
.get("Content-Length")
.and_then(|v| v.to_str().ok())
.and_then(|s| s.parse::<u64>().ok());
assert_eq!(
content_length,
Some(size as u64),
"Content-Length for {} bytes should be {}",
size,
size
);
let body = common::body_bytes(response).await;
assert_eq!(body.len(), size, "body length should match for {} bytes", size);
}
}
#[tokio::test]
async fn content_length_for_empty_file() {
let root = TempDir::new().unwrap();
fs::write(root.path().join("empty.txt"), b"").unwrap();
let server = Server::new(root.path()).unwrap();
let response = common::get(&server, "/empty.txt").await;
assert_eq!(response.status().as_u16(), 200);
let content_length = response
.headers()
.get("Content-Length")
.and_then(|v| v.to_str().ok())
.and_then(|s| s.parse::<u64>().ok());
assert_eq!(content_length, Some(0));
let body = common::body_bytes(response).await;
assert_eq!(body.len(), 0);
}
#[tokio::test]
async fn content_length_for_large_file() {
let root = TempDir::new().unwrap();
let large_size = 10_000_000u64; let content = vec![0u8; large_size as usize];
fs::write(root.path().join("large.bin"), &content).unwrap();
let server = Server::new(root.path()).unwrap();
let response = common::get(&server, "/large.bin").await;
assert_eq!(response.status().as_u16(), 200);
let content_length = response
.headers()
.get("Content-Length")
.and_then(|v| v.to_str().ok())
.and_then(|s| s.parse::<u64>().ok());
assert_eq!(content_length, Some(large_size));
let body = common::body_bytes(response).await;
assert_eq!(body.len() as u64, large_size);
}
#[tokio::test]
async fn head_method_includes_content_length_but_empty_body() {
let root = TempDir::new().unwrap();
let content = b"test content for head";
fs::write(root.path().join("file.txt"), content).unwrap();
let server = Server::new(root.path()).unwrap();
let response = common::request(&server, &Method::HEAD, "/file.txt").await;
assert_eq!(response.status().as_u16(), 200);
let content_length = response
.headers()
.get("Content-Length")
.and_then(|v| v.to_str().ok())
.and_then(|s| s.parse::<u64>().ok());
assert_eq!(content_length, Some(content.len() as u64));
let body = common::body_bytes(response).await;
assert_eq!(body.len(), 0, "HEAD response must not have a body");
}
#[tokio::test]
async fn content_length_with_directory_index() {
let root = TempDir::new().unwrap();
fs::create_dir(root.path().join("docs")).unwrap();
let index_content = b"<html><body>Index</body></html>";
fs::write(root.path().join("docs/index.html"), index_content).unwrap();
let server = Server::new(root.path()).unwrap();
let response = common::get(&server, "/docs/").await;
assert_eq!(response.status().as_u16(), 200);
let content_length = response
.headers()
.get("Content-Length")
.and_then(|v| v.to_str().ok())
.and_then(|s| s.parse::<u64>().ok());
assert_eq!(content_length, Some(index_content.len() as u64));
let body = common::body_bytes(response).await;
assert_eq!(&body[..], index_content);
}
#[tokio::test]
async fn content_length_with_special_chars_in_filename() {
let root = TempDir::new().unwrap();
let content = b"special file content";
let filename = "file with spaces.txt";
fs::write(root.path().join(filename), content).unwrap();
let server = Server::new(root.path()).unwrap();
let response =
common::get(&server, "/file%20with%20spaces.txt").await;
assert_eq!(response.status().as_u16(), 200);
let content_length = response
.headers()
.get("Content-Length")
.and_then(|v| v.to_str().ok())
.and_then(|s| s.parse::<u64>().ok());
assert_eq!(content_length, Some(content.len() as u64));
let body = common::body_bytes(response).await;
assert_eq!(&body[..], content);
}