use churust_core::{Churust, Header, HeaderName, Path, Query, TestClient};
use http::StatusCode;
use serde::Deserialize;
#[derive(Deserialize)]
struct Pager {
page: u32,
}
struct XTrace;
impl HeaderName for XTrace {
const NAME: &'static str = "x-trace";
}
fn app() -> churust_core::App {
Churust::server()
.routing(|r| {
r.get("/q", |q: Option<Query<Pager>>| async move {
match q {
Some(Query(p)) => format!("page {}", p.page),
None => "no query".to_string(),
}
});
r.get("/h", |h: Option<Header<String, XTrace>>| async move {
match h {
Some(Header(v, _)) => format!("trace {v}"),
None => "no trace".to_string(),
}
});
r.get("/p/{id}", |p: Option<Path<u64>>| async move {
match p {
Some(Path(id)) => format!("id {id}"),
None => "no id".to_string(),
}
});
r.post(
"/mixed",
|q: Option<Query<Pager>>, body: String| async move {
format!("{:?}/{}", q.map(|Query(p)| p.page), body)
},
);
})
.build()
}
async fn get(path: &str) -> (StatusCode, String) {
let res = TestClient::new(app()).get(path).send().await;
(res.status(), res.text())
}
#[tokio::test]
async fn a_present_value_extracts_as_some() {
let (status, body) = get("/q?page=7").await;
assert_eq!(status, StatusCode::OK, "{body}");
assert_eq!(body, "page 7");
}
#[tokio::test]
async fn an_absent_query_is_none() {
let (status, body) = get("/q").await;
assert_eq!(status, StatusCode::OK, "{body}");
assert_eq!(body, "no query");
}
#[tokio::test]
async fn a_malformed_query_is_still_an_error() {
let (status, _) = get("/q?page=notanumber").await;
assert_eq!(
status,
StatusCode::BAD_REQUEST,
"a malformed value was swallowed as None"
);
}
#[tokio::test]
async fn a_partial_query_is_still_an_error() {
let (status, _) = get("/q?other=1").await;
assert_eq!(status, StatusCode::BAD_REQUEST);
}
#[tokio::test]
async fn an_absent_header_is_none() {
let (status, body) = get("/h").await;
assert_eq!(status, StatusCode::OK, "{body}");
assert_eq!(body, "no trace");
}
#[tokio::test]
async fn a_present_header_is_some() {
let res = TestClient::new(app())
.get("/h")
.header("x-trace", "abc123")
.send()
.await;
assert_eq!(res.status(), StatusCode::OK);
assert_eq!(res.text(), "trace abc123");
}
#[tokio::test]
async fn a_present_path_parameter_is_some() {
let (status, body) = get("/p/42").await;
assert_eq!(status, StatusCode::OK, "{body}");
assert_eq!(body, "id 42");
}
#[tokio::test]
async fn an_unparseable_path_parameter_is_still_an_error() {
let (status, _) = get("/p/notanumber").await;
assert_eq!(status, StatusCode::BAD_REQUEST);
}
#[tokio::test]
async fn an_optional_extractor_composes_with_a_body_extractor() {
let res = TestClient::new(app())
.post("/mixed?page=3")
.body("hello")
.send()
.await;
assert_eq!(res.status(), StatusCode::OK);
assert_eq!(res.text(), "Some(3)/hello");
let res = TestClient::new(app())
.post("/mixed")
.body("hello")
.send()
.await;
assert_eq!(res.text(), "None/hello");
}