use flatbed::{static_route, Flatbed, FlatbedConfig};
use tokio::task::JoinHandle;
use tokio::time::{sleep, Duration};
mod common;
static_route!(mount = "/static", dir = "tests/static_fixture");
static_route!(
mount = "/embedded",
embed = "tests/static_fixture",
fallback = "index.html"
);
async fn boot(client: &reqwest::Client) -> (u16, JoinHandle<Result<(), flatbed::Error>>) {
let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
let port = listener.local_addr().unwrap().port();
drop(listener);
let config = FlatbedConfig::new("static-test")
.host("127.0.0.1")
.port(port);
let server = tokio::spawn(async move { Flatbed::run(config, |_| async { Ok(()) }).await });
let url = format!("http://127.0.0.1:{port}/static/app.js");
for _ in 0..100 {
if let Ok(r) = client.get(&url).send().await {
if r.status().as_u16() != 503 {
return (port, server);
}
}
sleep(Duration::from_millis(20)).await;
}
panic!("server did not become ready");
}
#[tokio::test]
async fn head_static_file_strips_body_keeps_length() {
let client = reqwest::Client::new();
let (port, server) = boot(&client).await;
let url = format!("http://127.0.0.1:{port}/static/app.js");
let get = client.get(&url).send().await.unwrap();
assert_eq!(get.status().as_u16(), 200);
assert_eq!(
get.headers().get("content-type").unwrap(),
"text/javascript; charset=utf-8"
);
let get_content_length = get
.headers()
.get("content-length")
.expect("GET content-length present")
.to_str()
.unwrap()
.to_string();
let head = client.head(&url).send().await.unwrap();
assert_eq!(head.status().as_u16(), 200);
assert_eq!(
head.headers()
.get("content-length")
.unwrap()
.to_str()
.unwrap(),
get_content_length,
"HEAD Content-Length must equal GET's over HTTP/1.1"
);
let (status, content_length, received) = common::h2c_head_request(port, "/static/app.js").await;
assert_eq!(status, http::StatusCode::OK, "HEAD should be 200 over h2c");
assert_eq!(
content_length.as_deref(),
Some(get_content_length.as_str()),
"HEAD content-length must equal GET's over h2c"
);
assert_eq!(received, 0, "HEAD must not put a body on the wire over h2c");
server.abort();
}
#[tokio::test]
async fn embedded_mount_serves_from_the_binary() {
let client = reqwest::Client::new();
let (port, server) = boot(&client).await;
let asset = client
.get(format!("http://127.0.0.1:{port}/embedded/app.js"))
.send()
.await
.unwrap();
assert_eq!(asset.status().as_u16(), 200);
assert_eq!(
asset.headers().get("content-type").unwrap(),
"text/javascript; charset=utf-8"
);
assert_eq!(
asset.headers().get("cache-control").unwrap(),
"public, max-age=31536000, immutable"
);
assert_eq!(
asset.bytes().await.unwrap().as_ref(),
std::fs::read("tests/static_fixture/app.js").unwrap()
);
let shell = client
.get(format!(
"http://127.0.0.1:{port}/embedded/some/client/route"
))
.send()
.await
.unwrap();
assert_eq!(shell.status().as_u16(), 200);
assert_eq!(
shell.headers().get("content-type").unwrap(),
"text/html; charset=utf-8"
);
assert!(shell.text().await.unwrap().contains("embedded shell"));
let missing = client
.get(format!("http://127.0.0.1:{port}/embedded/missing.js"))
.send()
.await
.unwrap();
assert_eq!(missing.status().as_u16(), 404);
server.abort();
}