mod common;
use hyper::Method;
use mini_static::Server;
use std::fs;
use tempfile::TempDir;
fn root_with_index(dir: &str) -> TempDir {
let root = TempDir::new().unwrap();
fs::create_dir_all(root.path().join(dir)).unwrap();
fs::write(
root.path().join(dir).join("index.html"),
b"<html>Index</html>",
)
.unwrap();
root
}
fn location(response: &hyper::Response<mini_static::ResponseBody>) -> Option<&str> {
response
.headers()
.get("Location")
.and_then(|v| v.to_str().ok())
}
#[tokio::test]
async fn directory_without_a_trailing_slash_redirects_to_one() {
let root = root_with_index("docs");
let server = Server::new(root.path()).unwrap();
let response = common::get(&server, "/docs").await;
assert_eq!(response.status().as_u16(), 301);
assert_eq!(location(&response), Some("/docs/"));
assert_eq!(
response
.headers()
.get("X-Content-Type-Options")
.and_then(|v| v.to_str().ok()),
Some("nosniff"),
"the redirect must still carry the nosniff header"
);
}
#[tokio::test]
async fn nested_directory_redirects_to_its_full_path_with_a_slash() {
let root = root_with_index("api/v1/users");
let server = Server::new(root.path()).unwrap();
let response = common::get(&server, "/api/v1/users").await;
assert_eq!(response.status().as_u16(), 301);
assert_eq!(location(&response), Some("/api/v1/users/"));
}
#[tokio::test]
async fn head_gets_the_same_redirect_as_get() {
let root = root_with_index("docs");
let server = Server::new(root.path()).unwrap();
let response = common::request(&server, &Method::HEAD, "/docs").await;
assert_eq!(response.status().as_u16(), 301);
assert_eq!(location(&response), Some("/docs/"));
}
#[tokio::test]
async fn redirect_location_preserves_percent_encoding() {
let root = root_with_index("my docs");
let server = Server::new(root.path()).unwrap();
let response = common::get(&server, "/my%20docs").await;
assert_eq!(response.status().as_u16(), 301);
assert_eq!(location(&response), Some("/my%20docs/"));
}
#[tokio::test]
async fn a_path_that_already_addresses_the_index_is_served_without_a_redirect() {
let root = root_with_index("docs");
let server = Server::new(root.path()).unwrap();
for path in ["/docs/", "/docs/index.html", "/docs/index.htm%6c"] {
let response = common::get(&server, path).await;
assert_eq!(
response.status().as_u16(),
200,
"{path} should be served directly"
);
assert!(location(&response).is_none(), "{path} should not redirect");
}
}
#[tokio::test]
async fn directory_without_an_index_html_returns_404() {
let root = TempDir::new().unwrap();
fs::create_dir(root.path().join("empty")).unwrap();
let server = Server::new(root.path()).unwrap();
let response = common::get(&server, "/empty").await;
assert_eq!(response.status().as_u16(), 404);
}