76 lines
2.7 KiB
Rust
76 lines
2.7 KiB
Rust
mod common;
|
|
|
|
use axum::http::StatusCode;
|
|
|
|
/// Fresh per-test downloads directory so parallel tests never share a file.
|
|
async fn server_with(downloads_dir: &std::path::Path) -> axum_test::TestServer {
|
|
let accounts = common::spawn_echo().await;
|
|
let configs = common::spawn_echo().await;
|
|
let mut cfg = common::config(&accounts, &configs);
|
|
cfg.downloads_dir = downloads_dir.display().to_string();
|
|
let app = gateway::build_app(&cfg, common::no_devices());
|
|
axum_test::TestServer::new(app)
|
|
}
|
|
|
|
/// A clean per-test directory inside the gateway's downloads root.
|
|
fn dir(name: &str) -> std::path::PathBuf {
|
|
let dir = std::env::temp_dir().join(format!("lv-downloads-{name}-{}", std::process::id()));
|
|
let _ = std::fs::remove_dir_all(&dir);
|
|
std::fs::create_dir_all(&dir).expect("create downloads dir");
|
|
dir
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn serves_the_mod_jar_as_a_download() {
|
|
let dir = dir("serve");
|
|
std::fs::write(dir.join("lovisual.jar"), b"fake-jar-bytes").expect("write jar");
|
|
let server = server_with(&dir).await;
|
|
|
|
let res = server.get("/downloads/lovisual.jar").await;
|
|
res.assert_status(StatusCode::OK);
|
|
assert_eq!(
|
|
res.headers()["content-type"],
|
|
"application/java-archive",
|
|
"the jar keeps its own media type"
|
|
);
|
|
assert_eq!(res.headers()["content-length"], "14");
|
|
assert_eq!(res.text(), "fake-jar-bytes");
|
|
std::fs::remove_dir_all(&dir).ok();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn missing_file_and_directory_index_are_404() {
|
|
let dir = dir("missing");
|
|
let server = server_with(&dir).await;
|
|
|
|
server
|
|
.get("/downloads/other.jar")
|
|
.await
|
|
.assert_status(StatusCode::NOT_FOUND);
|
|
// No index/SPA fallback under /downloads: a directory is never a download.
|
|
let res = server.get("/downloads/").await;
|
|
assert_ne!(
|
|
res.status_code(),
|
|
StatusCode::OK,
|
|
"directory must not be served"
|
|
);
|
|
std::fs::remove_dir_all(&dir).ok();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn dot_segment_traversal_is_rejected_before_the_file_is_touched() {
|
|
let dir = dir("traversal");
|
|
std::fs::write(dir.join("lovisual.jar"), b"fake-jar-bytes").expect("write jar");
|
|
let accounts = common::spawn_echo().await;
|
|
let configs = common::spawn_echo().await;
|
|
let mut cfg = common::config(&accounts, &configs);
|
|
cfg.downloads_dir = dir.display().to_string();
|
|
let app = gateway::build_app(&cfg, common::no_devices());
|
|
|
|
// Raw request: an HTTP client would normalize `..` away before sending.
|
|
let (status, _headers, body) =
|
|
common::raw(&app, "GET", "/downloads/../../etc/passwd", "1.2.3.4").await;
|
|
assert_eq!(status, StatusCode::BAD_REQUEST);
|
|
assert!(!body.contains("fake-jar-bytes"));
|
|
std::fs::remove_dir_all(&dir).ok();
|
|
}
|