Add testing example (#24)

This commit is contained in:
David Pedersen 2021-06-19 13:44:21 +02:00 committed by GitHub
parent 356f1c8424
commit 44577a0108
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 105 additions and 6 deletions

View file

@ -74,7 +74,7 @@ jobs:
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: test command: test
args: --all --all-features args: --all --all-features --all-targets
deny-check: deny-check:
name: cargo-deny check name: cargo-deny check

101
examples/testing.rs Normal file
View file

@ -0,0 +1,101 @@
use awebframework::{prelude::*, routing::BoxRoute};
use tower_http::trace::TraceLayer;
#[tokio::main]
async fn main() {
tracing_subscriber::fmt::init();
let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 3000));
tracing::debug!("listening on {}", addr);
hyper::Server::bind(&addr)
.serve(app().into_make_service())
.await
.unwrap();
}
/// Having a function that produces our app makes it easy to call it from tests
/// without having to create an HTTP server.
#[allow(dead_code)]
fn app() -> BoxRoute<Body> {
route("/", get(|| async { "Hello, World!" }))
.route(
"/json",
post(|payload: extract::Json<serde_json::Value>| async move {
response::Json(serde_json::json!({ "data": payload.0 }))
}),
)
// We can still add middleware
.layer(TraceLayer::new_for_http())
.boxed()
}
#[cfg(test)]
mod tests {
use super::*;
use http::StatusCode;
use serde_json::{json, Value};
use tower::ServiceExt; // for `app.oneshot()`
#[tokio::test]
async fn hello_world() {
let app = app();
// `BoxRoute<Body>` implements `tower::Service<Request<Body>>` so we can
// call it like any tower service, no need to run an HTTP server.
let response = app
.oneshot(Request::builder().uri("/").body(Body::empty()).unwrap())
.await
.unwrap();
assert_eq!(response.status(), StatusCode::OK);
let body = hyper::body::to_bytes(response.into_body()).await.unwrap();
assert_eq!(&body[..], b"Hello, World!");
}
#[tokio::test]
async fn json() {
let app = app();
let response = app
.oneshot(
Request::builder()
.method(http::Method::POST)
.uri("/json")
.header(http::header::CONTENT_TYPE, "application/json")
.body(Body::from(
serde_json::to_vec(&json!([1, 2, 3, 4])).unwrap(),
))
.unwrap(),
)
.await
.unwrap();
assert_eq!(response.status(), StatusCode::OK);
let body = hyper::body::to_bytes(response.into_body()).await.unwrap();
let body: Value = serde_json::from_slice(&body).unwrap();
assert_eq!(body, json!({ "data": [1, 2, 3, 4] }));
}
#[tokio::test]
async fn not_found() {
let app = app();
let response = app
.oneshot(
Request::builder()
.uri("/does-not-exist")
.body(Body::empty())
.unwrap(),
)
.await
.unwrap();
assert_eq!(response.status(), StatusCode::NOT_FOUND);
let body = hyper::body::to_bytes(response.into_body()).await.unwrap();
assert!(body.is_empty());
}
}

View file

@ -1,10 +1,8 @@
//! Rejection response types. //! Rejection response types.
use http::StatusCode;
use tower::BoxError;
use super::IntoResponse; use super::IntoResponse;
use crate::body::Body; use crate::body::Body;
use tower::BoxError;
macro_rules! define_rejection { macro_rules! define_rejection {
( (
@ -355,9 +353,9 @@ pub struct TypedHeaderRejection {
#[cfg(feature = "headers")] #[cfg(feature = "headers")]
#[cfg_attr(docsrs, doc(cfg(feature = "headers")))] #[cfg_attr(docsrs, doc(cfg(feature = "headers")))]
impl IntoResponse for TypedHeaderRejection { impl IntoResponse for TypedHeaderRejection {
fn into_response(self) -> http::Response<Body> { fn into_response(self) -> http::Response<crate::Body> {
let mut res = format!("{} ({})", self.err, self.name).into_response(); let mut res = format!("{} ({})", self.err, self.name).into_response();
*res.status_mut() = StatusCode::BAD_REQUEST; *res.status_mut() = http::StatusCode::BAD_REQUEST;
res res
} }
} }