use axum::body::Body;
use axum::http::{HeaderValue, Method, Request, StatusCode, header};
use axum::response::Response;
use bytes::Bytes;
use futures_core::Stream;
use serde_json::Value;
use std::pin::Pin;
use std::task::{Context, Poll};
pub async fn request_as(
router: &axum::Router,
method: Method,
path: &str,
bearer: &str,
json: Option<&str>,
) -> TestResponse {
use tower::ServiceExt;
let mut builder = Request::builder()
.method(method)
.uri(path)
.header(header::AUTHORIZATION, format!("Bearer {bearer}"));
let body = match json {
Some(payload) => {
builder = builder.header(
header::CONTENT_TYPE,
HeaderValue::from_static("application/json"),
);
Body::from(payload.to_owned())
}
None => Body::empty(),
};
let request = builder.body(body).expect("request builds");
let response = router
.clone()
.oneshot(request)
.await
.expect("router answers");
TestResponse::from(response).await
}
pub async fn request(
router: &axum::Router,
method: Method,
path: &str,
json: Option<&str>,
) -> TestResponse {
use tower::ServiceExt;
let mut builder = Request::builder().method(method).uri(path);
let body = match json {
Some(payload) => {
builder = builder.header(
header::CONTENT_TYPE,
HeaderValue::from_static("application/json"),
);
Body::from(payload.to_owned())
}
None => Body::empty(),
};
let request = builder.body(body).expect("request builds");
let response = router
.clone()
.oneshot(request)
.await
.expect("router answers");
TestResponse::from(response).await
}
pub async fn request_chunks(
router: &axum::Router,
method: Method,
path: &str,
chunks: Vec<Bytes>,
) -> TestResponse {
use tower::ServiceExt;
let body = Body::from_stream(Chunks {
items: chunks.into_iter(),
});
let request = Request::builder()
.method(method)
.uri(path)
.header(
header::CONTENT_TYPE,
HeaderValue::from_static("application/octet-stream"),
)
.body(body)
.expect("request builds");
let response = router
.clone()
.oneshot(request)
.await
.expect("router answers");
TestResponse::from(response).await
}
struct Chunks {
items: std::vec::IntoIter<Bytes>,
}
impl Stream for Chunks {
type Item = Result<Bytes, std::convert::Infallible>;
fn poll_next(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
Poll::Ready(self.get_mut().items.next().map(Ok))
}
}
pub struct TestResponse {
pub status: StatusCode,
pub headers: axum::http::HeaderMap,
body: Bytes,
}
impl TestResponse {
pub(crate) async fn of(response: Response) -> Self {
Self::from(response).await
}
async fn from(response: Response) -> Self {
let (parts, body) = response.into_parts();
let body = axum::body::to_bytes(body, 1024 * 1024)
.await
.expect("test body reads");
Self {
status: parts.status,
headers: parts.headers,
body,
}
}
#[must_use]
pub fn json(&self) -> Value {
serde_json::from_slice(&self.body).expect("body is JSON")
}
#[must_use]
pub fn body(&self) -> &Bytes {
&self.body
}
}