From 0b9e0c75080b3ab2075bd5e4867baa60e2f4d8b2 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sun, 19 Sep 2021 11:38:34 +0200 Subject: [PATCH] Refactor internal testing setup (#338) This changes the test setup to use our own client rather than using reqwest directly. Allows us to add several quality of life features like always unwrapping results. --- src/extract/request_parts.rs | 13 +- src/extract/typed_header.rs | 17 +- src/tests/handle_error.rs | 40 +--- src/tests/helpers.rs | 140 +++++++++++++ src/tests/mod.rs | 395 +++++++++-------------------------- src/tests/nest.rs | 132 +++--------- src/tests/or.rs | 246 +++++----------------- 7 files changed, 338 insertions(+), 645 deletions(-) create mode 100644 src/tests/helpers.rs diff --git a/src/extract/request_parts.rs b/src/extract/request_parts.rs index e7b68951..22c1c517 100644 --- a/src/extract/request_parts.rs +++ b/src/extract/request_parts.rs @@ -335,19 +335,12 @@ mod tests { let app = Router::new().route("/", post(handler)); - let addr = run_in_background(app).await; + let client = TestClient::new(app); - let client = reqwest::Client::new(); - - let res = client - .post(format!("http://{}", addr)) - .body("hi there") - .send() - .await - .unwrap(); + let res = client.post("/").body("hi there").send().await; assert_eq!(res.status(), StatusCode::INTERNAL_SERVER_ERROR); assert_eq!( - res.text().await.unwrap(), + res.text().await, "Cannot have two request body extractors for a single handler" ); } diff --git a/src/extract/typed_header.rs b/src/extract/typed_header.rs index 276ea208..b1f6945c 100644 --- a/src/extract/typed_header.rs +++ b/src/extract/typed_header.rs @@ -152,21 +152,14 @@ mod tests { let app = Router::new().route("/", get(handle)); - let addr = run_in_background(app).await; + let client = TestClient::new(app); - let client = reqwest::Client::new(); - - let res = client - .get(format!("http://{}", addr)) - .header("user-agent", "foobar") - .send() - .await - .unwrap(); - let body = res.text().await.unwrap(); + let res = client.get("/").header("user-agent", "foobar").send().await; + let body = res.text().await; assert_eq!(body, "foobar"); - let res = client.get(format!("http://{}", addr)).send().await.unwrap(); - let body = res.text().await.unwrap(); + let res = client.get("/").send().await; + let body = res.text().await; assert_eq!(body, "Header of type `user-agent` was missing"); } } diff --git a/src/tests/handle_error.rs b/src/tests/handle_error.rs index 6af66033..eec54946 100644 --- a/src/tests/handle_error.rs +++ b/src/tests/handle_error.rs @@ -48,15 +48,9 @@ async fn handler() { .handle_error(|_: BoxError| Ok::<_, Infallible>(StatusCode::REQUEST_TIMEOUT))), ); - let addr = run_in_background(app).await; + let client = TestClient::new(app); - let client = reqwest::Client::new(); - - let res = client - .get(format!("http://{}/", addr)) - .send() - .await - .unwrap(); + let res = client.get("/").send().await; assert_eq!(res.status(), StatusCode::REQUEST_TIMEOUT); } @@ -70,15 +64,9 @@ async fn handler_multiple_methods_first() { .post(unit), ); - let addr = run_in_background(app).await; + let client = TestClient::new(app); - let client = reqwest::Client::new(); - - let res = client - .get(format!("http://{}/", addr)) - .send() - .await - .unwrap(); + let res = client.get("/").send().await; assert_eq!(res.status(), StatusCode::REQUEST_TIMEOUT); } @@ -95,15 +83,9 @@ async fn handler_multiple_methods_middle() { .post(unit), ); - let addr = run_in_background(app).await; + let client = TestClient::new(app); - let client = reqwest::Client::new(); - - let res = client - .get(format!("http://{}/", addr)) - .send() - .await - .unwrap(); + let res = client.get("/").send().await; assert_eq!(res.status(), StatusCode::REQUEST_TIMEOUT); } @@ -118,15 +100,9 @@ async fn handler_multiple_methods_last() { ), ); - let addr = run_in_background(app).await; + let client = TestClient::new(app); - let client = reqwest::Client::new(); - - let res = client - .get(format!("http://{}/", addr)) - .send() - .await - .unwrap(); + let res = client.get("/").send().await; assert_eq!(res.status(), StatusCode::REQUEST_TIMEOUT); } diff --git a/src/tests/helpers.rs b/src/tests/helpers.rs new file mode 100644 index 00000000..34fb2dd2 --- /dev/null +++ b/src/tests/helpers.rs @@ -0,0 +1,140 @@ +#![allow(unused_imports, dead_code)] + +use crate::BoxError; +use crate::{ + extract, + handler::{any, delete, get, on, patch, post, Handler}, + response::IntoResponse, + routing::MethodFilter, + service, Router, +}; +use bytes::Bytes; +use http::{ + header::{HeaderMap, HeaderName, HeaderValue, AUTHORIZATION}, + Method, Request, StatusCode, Uri, +}; +use hyper::{Body, Server}; +use serde::Deserialize; +use serde_json::json; +use std::future::Ready; +use std::{ + collections::HashMap, + convert::{Infallible, TryFrom}, + future::ready, + net::{SocketAddr, TcpListener}, + task::{Context, Poll}, + time::Duration, +}; +use tower::{make::Shared, service_fn}; +use tower_service::Service; + +pub(crate) struct TestClient { + client: reqwest::Client, + addr: SocketAddr, +} + +impl TestClient { + pub(crate) fn new(svc: S) -> Self + where + S: Service, Response = http::Response> + Clone + Send + 'static, + ResBody: http_body::Body + Send + 'static, + ResBody::Data: Send, + ResBody::Error: Into, + S::Future: Send, + S::Error: Into, + { + let listener = TcpListener::bind("127.0.0.1:0").expect("Could not bind ephemeral socket"); + let addr = listener.local_addr().unwrap(); + println!("Listening on {}", addr); + + tokio::spawn(async move { + let server = Server::from_tcp(listener).unwrap().serve(Shared::new(svc)); + server.await.expect("server error"); + }); + + TestClient { + client: reqwest::Client::new(), + addr, + } + } + + pub(crate) fn get(&self, url: &str) -> RequestBuilder { + RequestBuilder { + builder: self.client.get(format!("http://{}{}", self.addr, url)), + } + } + + pub(crate) fn post(&self, url: &str) -> RequestBuilder { + RequestBuilder { + builder: self.client.post(format!("http://{}{}", self.addr, url)), + } + } + + pub(crate) fn put(&self, url: &str) -> RequestBuilder { + RequestBuilder { + builder: self.client.put(format!("http://{}{}", self.addr, url)), + } + } + + pub(crate) fn patch(&self, url: &str) -> RequestBuilder { + RequestBuilder { + builder: self.client.patch(format!("http://{}{}", self.addr, url)), + } + } +} + +pub(crate) struct RequestBuilder { + builder: reqwest::RequestBuilder, +} + +impl RequestBuilder { + pub(crate) async fn send(self) -> Response { + Response { + response: self.builder.send().await.unwrap(), + } + } + + pub(crate) fn body(mut self, body: impl Into) -> Self { + self.builder = self.builder.body(body); + self + } + + pub(crate) fn json(mut self, json: &T) -> Self + where + T: serde::Serialize, + { + self.builder = self.builder.json(json); + self + } + pub(crate) fn header(mut self, key: K, value: V) -> Self + where + HeaderName: TryFrom, + >::Error: Into, + HeaderValue: TryFrom, + >::Error: Into, + { + self.builder = self.builder.header(key, value); + self + } +} + +pub(crate) struct Response { + response: reqwest::Response, +} + +impl Response { + pub(crate) async fn text(self) -> String { + self.response.text().await.unwrap() + } + + pub(crate) async fn json(self) -> T + where + T: serde::de::DeserializeOwned, + { + self.response.json().await.unwrap() + } + + pub(crate) fn status(&self) -> StatusCode { + self.response.status() + } +} diff --git a/src/tests/mod.rs b/src/tests/mod.rs index b578b6d8..6a8f0255 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -2,7 +2,7 @@ use crate::BoxError; use crate::{ - extract, + extract::{self, Path}, handler::{any, delete, get, on, patch, post, Handler}, response::IntoResponse, routing::MethodFilter, @@ -13,7 +13,7 @@ use http::{ header::{HeaderMap, AUTHORIZATION}, Request, Response, StatusCode, Uri, }; -use hyper::{Body, Server}; +use hyper::Body; use serde::Deserialize; use serde_json::json; use std::future::Ready; @@ -21,15 +21,17 @@ use std::{ collections::HashMap, convert::Infallible, future::ready, - net::{SocketAddr, TcpListener}, task::{Context, Poll}, time::Duration, }; -use tower::{make::Shared, service_fn}; +use tower::service_fn; use tower_service::Service; +pub(crate) use helpers::*; + mod get_to_head; mod handle_error; +mod helpers; mod nest; mod or; @@ -51,28 +53,18 @@ async fn hello_world() { .route("/", get(root).post(foo)) .route("/users", post(users_create)); - let addr = run_in_background(app).await; + let client = TestClient::new(app); - let client = reqwest::Client::new(); - - let res = client.get(format!("http://{}", addr)).send().await.unwrap(); - let body = res.text().await.unwrap(); + let res = client.get("/").send().await; + let body = res.text().await; assert_eq!(body, "Hello, World!"); - let res = client - .post(format!("http://{}", addr)) - .send() - .await - .unwrap(); - let body = res.text().await.unwrap(); + let res = client.post("/").send().await; + let body = res.text().await; assert_eq!(body, "foo"); - let res = client - .post(format!("http://{}/users", addr)) - .send() - .await - .unwrap(); - let body = res.text().await.unwrap(); + let res = client.post("/users").send().await; + let body = res.text().await; assert_eq!(body, "users#create"); } @@ -80,16 +72,9 @@ async fn hello_world() { async fn consume_body() { let app = Router::new().route("/", get(|body: String| async { body })); - let addr = run_in_background(app).await; - - let client = reqwest::Client::new(); - let res = client - .get(format!("http://{}", addr)) - .body("foo") - .send() - .await - .unwrap(); - let body = res.text().await.unwrap(); + let client = TestClient::new(app); + let res = client.get("/").body("foo").send().await; + let body = res.text().await; assert_eq!(body, "foo"); } @@ -106,16 +91,9 @@ async fn deserialize_body() { post(|input: extract::Json| async { input.0.foo }), ); - let addr = run_in_background(app).await; - - let client = reqwest::Client::new(); - let res = client - .post(format!("http://{}", addr)) - .json(&json!({ "foo": "bar" })) - .send() - .await - .unwrap(); - let body = res.text().await.unwrap(); + let client = TestClient::new(app); + let res = client.post("/").json(&json!({ "foo": "bar" })).send().await; + let body = res.text().await; assert_eq!(body, "bar"); } @@ -132,18 +110,11 @@ async fn consume_body_to_json_requires_json_content_type() { post(|input: extract::Json| async { input.0.foo }), ); - let addr = run_in_background(app).await; - - let client = reqwest::Client::new(); - let res = client - .post(format!("http://{}", addr)) - .body(r#"{ "foo": "bar" }"#) - .send() - .await - .unwrap(); + let client = TestClient::new(app); + let res = client.post("/").body(r#"{ "foo": "bar" }"#).send().await; let status = res.status(); - dbg!(res.text().await.unwrap()); + dbg!(res.text().await); assert_eq!(status, StatusCode::BAD_REQUEST); } @@ -164,42 +135,35 @@ async fn body_with_length_limit() { post(|_body: extract::ContentLengthLimit| async {}), ); - let addr = run_in_background(app).await; - - let client = reqwest::Client::new(); - + let client = TestClient::new(app); let res = client - .post(format!("http://{}", addr)) + .post("/") .body(repeat(0_u8).take((LIMIT - 1) as usize).collect::>()) .send() - .await - .unwrap(); + .await; assert_eq!(res.status(), StatusCode::OK); let res = client - .post(format!("http://{}", addr)) + .post("/") .body(repeat(0_u8).take(LIMIT as usize).collect::>()) .send() - .await - .unwrap(); + .await; assert_eq!(res.status(), StatusCode::OK); let res = client - .post(format!("http://{}", addr)) + .post("/") .body(repeat(0_u8).take((LIMIT + 1) as usize).collect::>()) .send() - .await - .unwrap(); + .await; assert_eq!(res.status(), StatusCode::PAYLOAD_TOO_LARGE); let res = client - .post(format!("http://{}", addr)) + .post("/") .body(reqwest::Body::wrap_stream(futures_util::stream::iter( vec![Ok::<_, std::io::Error>(bytes::Bytes::new())], ))) .send() - .await - .unwrap(); + .await; assert_eq!(res.status(), StatusCode::LENGTH_REQUIRED); } @@ -217,76 +181,46 @@ async fn routing() { get(|_: Request| async { "users#action" }), ); - let addr = run_in_background(app).await; + let client = TestClient::new(app); - let client = reqwest::Client::new(); - - let res = client.get(format!("http://{}", addr)).send().await.unwrap(); + let res = client.get("/").send().await; assert_eq!(res.status(), StatusCode::NOT_FOUND); - let res = client - .get(format!("http://{}/users", addr)) - .send() - .await - .unwrap(); + let res = client.get("/users").send().await; assert_eq!(res.status(), StatusCode::OK); - assert_eq!(res.text().await.unwrap(), "users#index"); + assert_eq!(res.text().await, "users#index"); - let res = client - .post(format!("http://{}/users", addr)) - .send() - .await - .unwrap(); + let res = client.post("/users").send().await; assert_eq!(res.status(), StatusCode::OK); - assert_eq!(res.text().await.unwrap(), "users#create"); + assert_eq!(res.text().await, "users#create"); - let res = client - .get(format!("http://{}/users/1", addr)) - .send() - .await - .unwrap(); + let res = client.get("/users/1").send().await; assert_eq!(res.status(), StatusCode::OK); - assert_eq!(res.text().await.unwrap(), "users#show"); + assert_eq!(res.text().await, "users#show"); - let res = client - .get(format!("http://{}/users/1/action", addr)) - .send() - .await - .unwrap(); + let res = client.get("/users/1/action").send().await; assert_eq!(res.status(), StatusCode::OK); - assert_eq!(res.text().await.unwrap(), "users#action"); + assert_eq!(res.text().await, "users#action"); } #[tokio::test] async fn extracting_url_params() { let app = Router::new().route( "/users/:id", - get(|extract::Path(id): extract::Path| async move { + get(|Path(id): Path| async move { assert_eq!(id, 42); }) - .post( - |extract::Path(params_map): extract::Path>| async move { - assert_eq!(params_map.get("id").unwrap(), &1337); - }, - ), + .post(|Path(params_map): Path>| async move { + assert_eq!(params_map.get("id").unwrap(), &1337); + }), ); - let addr = run_in_background(app).await; + let client = TestClient::new(app); - let client = reqwest::Client::new(); - - let res = client - .get(format!("http://{}/users/42", addr)) - .send() - .await - .unwrap(); + let res = client.get("/users/42").send().await; assert_eq!(res.status(), StatusCode::OK); - let res = client - .post(format!("http://{}/users/1337", addr)) - .send() - .await - .unwrap(); + let res = client.post("/users/1337").send().await; assert_eq!(res.status(), StatusCode::OK); } @@ -297,15 +231,9 @@ async fn extracting_url_params_multiple_times() { get(|_: extract::Path, _: extract::Path| async {}), ); - let addr = run_in_background(app).await; + let client = TestClient::new(app); - let client = reqwest::Client::new(); - - let res = client - .get(format!("http://{}/users/42", addr)) - .send() - .await - .unwrap(); + let res = client.get("/users/42").send().await; assert_eq!(res.status(), StatusCode::OK); } @@ -324,21 +252,15 @@ async fn boxing() { .layer(tower_http::compression::CompressionLayer::new()) .boxed(); - let addr = run_in_background(app).await; + let client = TestClient::new(app); - let client = reqwest::Client::new(); - - let res = client.get(format!("http://{}", addr)).send().await.unwrap(); + let res = client.get("/").send().await; assert_eq!(res.status(), StatusCode::OK); - assert_eq!(res.text().await.unwrap(), "hi from GET"); + assert_eq!(res.text().await, "hi from GET"); - let res = client - .post(format!("http://{}", addr)) - .send() - .await - .unwrap(); + let res = client.post("/").send().await; assert_eq!(res.status(), StatusCode::OK); - assert_eq!(res.text().await.unwrap(), "hi from POST"); + assert_eq!(res.text().await, "hi from POST"); } #[tokio::test] @@ -368,41 +290,23 @@ async fn routing_between_services() { ) .route("/two", service::on(MethodFilter::GET, any(handle))); - let addr = run_in_background(app).await; + let client = TestClient::new(app); - let client = reqwest::Client::new(); - - let res = client - .get(format!("http://{}/one", addr)) - .send() - .await - .unwrap(); + let res = client.get("/one").send().await; assert_eq!(res.status(), StatusCode::OK); - assert_eq!(res.text().await.unwrap(), "one get"); + assert_eq!(res.text().await, "one get"); - let res = client - .post(format!("http://{}/one", addr)) - .send() - .await - .unwrap(); + let res = client.post("/one").send().await; assert_eq!(res.status(), StatusCode::OK); - assert_eq!(res.text().await.unwrap(), "one post"); + assert_eq!(res.text().await, "one post"); - let res = client - .put(format!("http://{}/one", addr)) - .send() - .await - .unwrap(); + let res = client.put("/one").send().await; assert_eq!(res.status(), StatusCode::OK); - assert_eq!(res.text().await.unwrap(), "one put"); + assert_eq!(res.text().await, "one put"); - let res = client - .get(format!("http://{}/two", addr)) - .send() - .await - .unwrap(); + let res = client.get("/two").send().await; assert_eq!(res.status(), StatusCode::OK); - assert_eq!(res.text().await.unwrap(), "handler"); + assert_eq!(res.text().await, "handler"); } #[tokio::test] @@ -424,23 +328,23 @@ async fn middleware_on_single_route() { )), ); - let addr = run_in_background(app).await; + let client = TestClient::new(app); - let res = reqwest::get(format!("http://{}", addr)).await.unwrap(); - let body = res.text().await.unwrap(); + let res = client.get("/").send().await; + let body = res.text().await; assert_eq!(body, "Hello, World!"); } #[tokio::test] async fn service_in_bottom() { - async fn handler(_req: Request) -> Result, hyper::Error> { + async fn handler(_req: Request) -> Result, hyper::Error> { Ok(Response::new(hyper::Body::empty())) } let app = Router::new().route("/", service::get(service_fn(handler))); - run_in_background(app).await; + TestClient::new(app); } #[tokio::test] @@ -477,23 +381,12 @@ async fn test_extractor_middleware() { get(handler.layer(extract::extractor_middleware::())), ); - let addr = run_in_background(app).await; + let client = TestClient::new(app); - let client = reqwest::Client::new(); - - let res = client - .get(format!("http://{}/", addr)) - .send() - .await - .unwrap(); + let res = client.get("/").send().await; assert_eq!(res.status(), StatusCode::UNAUTHORIZED); - let res = client - .get(format!("http://{}/", addr)) - .header(AUTHORIZATION, "secret") - .send() - .await - .unwrap(); + let res = client.get("/").header(AUTHORIZATION, "secret").send().await; assert_eq!(res.status(), StatusCode::OK); } @@ -503,36 +396,18 @@ async fn wrong_method_handler() { .route("/", get(|| async {}).post(|| async {})) .route("/foo", patch(|| async {})); - let addr = run_in_background(app).await; + let client = TestClient::new(app); - let client = reqwest::Client::new(); - - let res = client - .patch(format!("http://{}", addr)) - .send() - .await - .unwrap(); + let res = client.patch("/").send().await; assert_eq!(res.status(), StatusCode::METHOD_NOT_ALLOWED); - let res = client - .patch(format!("http://{}/foo", addr)) - .send() - .await - .unwrap(); + let res = client.patch("/foo").send().await; assert_eq!(res.status(), StatusCode::OK); - let res = client - .post(format!("http://{}/foo", addr)) - .send() - .await - .unwrap(); + let res = client.post("/foo").send().await; assert_eq!(res.status(), StatusCode::METHOD_NOT_ALLOWED); - let res = client - .get(format!("http://{}/bar", addr)) - .send() - .await - .unwrap(); + let res = client.get("/bar").send().await; assert_eq!(res.status(), StatusCode::NOT_FOUND); } @@ -559,36 +434,18 @@ async fn wrong_method_service() { .route("/", service::get(Svc).post(Svc)) .route("/foo", service::patch(Svc)); - let addr = run_in_background(app).await; + let client = TestClient::new(app); - let client = reqwest::Client::new(); - - let res = client - .patch(format!("http://{}", addr)) - .send() - .await - .unwrap(); + let res = client.patch("/").send().await; assert_eq!(res.status(), StatusCode::METHOD_NOT_ALLOWED); - let res = client - .patch(format!("http://{}/foo", addr)) - .send() - .await - .unwrap(); + let res = client.patch("/foo").send().await; assert_eq!(res.status(), StatusCode::OK); - let res = client - .post(format!("http://{}/foo", addr)) - .send() - .await - .unwrap(); + let res = client.post("/foo").send().await; assert_eq!(res.status(), StatusCode::METHOD_NOT_ALLOWED); - let res = client - .get(format!("http://{}/bar", addr)) - .send() - .await - .unwrap(); + let res = client.get("/bar").send().await; assert_eq!(res.status(), StatusCode::NOT_FOUND); } @@ -600,18 +457,12 @@ async fn multiple_methods_for_one_handler() { let app = Router::new().route("/", on(MethodFilter::GET | MethodFilter::POST, root)); - let addr = run_in_background(app).await; + let client = TestClient::new(app); - let client = reqwest::Client::new(); - - let res = client.get(format!("http://{}", addr)).send().await.unwrap(); + let res = client.get("/").send().await; assert_eq!(res.status(), StatusCode::OK); - let res = client - .post(format!("http://{}", addr)) - .send() - .await - .unwrap(); + let res = client.post("/").send().await; assert_eq!(res.status(), StatusCode::OK); } @@ -621,18 +472,11 @@ async fn handler_into_service() { format!("you said: {}", body) } - let addr = run_in_background(handle.into_service()).await; + let client = TestClient::new(handle.into_service()); - let client = reqwest::Client::new(); - - let res = client - .post(format!("http://{}", addr)) - .body("hi there!") - .send() - .await - .unwrap(); + let res = client.post("/").body("hi there!").send().await; assert_eq!(res.status(), StatusCode::OK); - assert_eq!(res.text().await.unwrap(), "you said: hi there!"); + assert_eq!(res.text().await, "you said: hi there!"); } #[tokio::test] @@ -643,32 +487,18 @@ async fn when_multiple_routes_match() { .route("/foo", get(|| async {})) .nest("/foo", Router::new().route("/bar", get(|| async {}))); - let addr = run_in_background(app).await; + let client = TestClient::new(app); - let client = reqwest::Client::new(); - - let res = client.get(format!("http://{}", addr)).send().await.unwrap(); + let res = client.get("/").send().await; assert_eq!(res.status(), StatusCode::OK); - let res = client - .post(format!("http://{}", addr)) - .send() - .await - .unwrap(); + let res = client.post("/").send().await; assert_eq!(res.status(), StatusCode::OK); - let res = client - .get(format!("http://{}/foo/bar", addr)) - .send() - .await - .unwrap(); + let res = client.get("/foo/bar").send().await; assert_eq!(res.status(), StatusCode::OK); - let res = client - .get(format!("http://{}/foo", addr)) - .send() - .await - .unwrap(); + let res = client.get("/foo").send().await; assert_eq!(res.status(), StatusCode::OK); } @@ -676,48 +506,15 @@ async fn when_multiple_routes_match() { async fn captures_dont_match_empty_segments() { let app = Router::new().route("/:key", get(|| async {})); - let addr = run_in_background(app).await; + let client = TestClient::new(app); - let client = reqwest::Client::new(); - - let res = client.get(format!("http://{}", addr)).send().await.unwrap(); + let res = client.get("/").send().await; assert_eq!(res.status(), StatusCode::NOT_FOUND); - let res = client - .get(format!("http://{}/foo", addr)) - .send() - .await - .unwrap(); + let res = client.get("/foo").send().await; assert_eq!(res.status(), StatusCode::OK); } -/// Run a `tower::Service` in the background and get a URI for it. -pub(crate) async fn run_in_background(svc: S) -> SocketAddr -where - S: Service, Response = Response> + Clone + Send + 'static, - ResBody: http_body::Body + Send + 'static, - ResBody::Data: Send, - ResBody::Error: Into, - S::Future: Send, - S::Error: Into, -{ - let listener = TcpListener::bind("127.0.0.1:0").expect("Could not bind ephemeral socket"); - let addr = listener.local_addr().unwrap(); - println!("Listening on {}", addr); - - let (tx, rx) = tokio::sync::oneshot::channel(); - - tokio::spawn(async move { - let server = Server::from_tcp(listener).unwrap().serve(Shared::new(svc)); - tx.send(()).unwrap(); - server.await.expect("server error"); - }); - - rx.await.unwrap(); - - addr -} - pub(crate) fn assert_send() {} pub(crate) fn assert_sync() {} pub(crate) fn assert_unpin() {} diff --git a/src/tests/nest.rs b/src/tests/nest.rs index fc8b4417..271cc416 100644 --- a/src/tests/nest.rs +++ b/src/tests/nest.rs @@ -38,41 +38,23 @@ async fn nesting_apps() { .route("/", get(|| async { "hi" })) .nest("/:version/api", api_routes); - let addr = run_in_background(app).await; + let client = TestClient::new(app); - let client = reqwest::Client::new(); - - let res = client - .get(format!("http://{}/", addr)) - .send() - .await - .unwrap(); + let res = client.get("/").send().await; assert_eq!(res.status(), StatusCode::OK); - assert_eq!(res.text().await.unwrap(), "hi"); + assert_eq!(res.text().await, "hi"); - let res = client - .get(format!("http://{}/v0/api/users", addr)) - .send() - .await - .unwrap(); + let res = client.get("/v0/api/users").send().await; assert_eq!(res.status(), StatusCode::OK); - assert_eq!(res.text().await.unwrap(), "users#index"); + assert_eq!(res.text().await, "users#index"); - let res = client - .get(format!("http://{}/v0/api/users/123", addr)) - .send() - .await - .unwrap(); + let res = client.get("/v0/api/users/123").send().await; assert_eq!(res.status(), StatusCode::OK); - assert_eq!(res.text().await.unwrap(), "v0: users#show (123)"); + assert_eq!(res.text().await, "v0: users#show (123)"); - let res = client - .get(format!("http://{}/v0/api/games/123", addr)) - .send() - .await - .unwrap(); + let res = client.get("/v0/api/games/123").send().await; assert_eq!(res.status(), StatusCode::OK); - assert_eq!(res.text().await.unwrap(), "v0: games#show (123)"); + assert_eq!(res.text().await, "v0: games#show (123)"); } #[tokio::test] @@ -80,25 +62,15 @@ async fn wrong_method_nest() { let nested_app = Router::new().route("/", get(|| async {})); let app = Router::new().nest("/", nested_app); - let addr = run_in_background(app).await; + let client = TestClient::new(app); - let client = reqwest::Client::new(); - - let res = client.get(format!("http://{}", addr)).send().await.unwrap(); + let res = client.get("/").send().await; assert_eq!(res.status(), StatusCode::OK); - let res = client - .post(format!("http://{}", addr)) - .send() - .await - .unwrap(); + let res = client.post("/").send().await; assert_eq!(res.status(), StatusCode::METHOD_NOT_ALLOWED); - let res = client - .patch(format!("http://{}/foo", addr)) - .send() - .await - .unwrap(); + let res = client.patch("/foo").send().await; assert_eq!(res.status(), StatusCode::NOT_FOUND); } @@ -106,29 +78,19 @@ async fn wrong_method_nest() { async fn nesting_at_root() { let app = Router::new().nest("/", get(|uri: Uri| async move { uri.to_string() })); - let addr = run_in_background(app).await; + let client = TestClient::new(app); - let client = reqwest::Client::new(); - - let res = client.get(format!("http://{}", addr)).send().await.unwrap(); + let res = client.get("/").send().await; assert_eq!(res.status(), StatusCode::OK); - assert_eq!(res.text().await.unwrap(), "/"); + assert_eq!(res.text().await, "/"); - let res = client - .get(format!("http://{}/foo", addr)) - .send() - .await - .unwrap(); + let res = client.get("/foo").send().await; assert_eq!(res.status(), StatusCode::OK); - assert_eq!(res.text().await.unwrap(), "/foo"); + assert_eq!(res.text().await, "/foo"); - let res = client - .get(format!("http://{}/foo/bar", addr)) - .send() - .await - .unwrap(); + let res = client.get("/foo/bar").send().await; assert_eq!(res.status(), StatusCode::OK); - assert_eq!(res.text().await.unwrap(), "/foo/bar"); + assert_eq!(res.text().await, "/foo/bar"); } #[tokio::test] @@ -146,25 +108,15 @@ async fn nested_url_extractor() { ), ); - let addr = run_in_background(app).await; + let client = TestClient::new(app); - let client = reqwest::Client::new(); - - let res = client - .get(format!("http://{}/foo/bar/baz", addr)) - .send() - .await - .unwrap(); + let res = client.get("/foo/bar/baz").send().await; assert_eq!(res.status(), StatusCode::OK); - assert_eq!(res.text().await.unwrap(), "/baz"); + assert_eq!(res.text().await, "/baz"); - let res = client - .get(format!("http://{}/foo/bar/qux", addr)) - .send() - .await - .unwrap(); + let res = client.get("/foo/bar/qux").send().await; assert_eq!(res.status(), StatusCode::OK); - assert_eq!(res.text().await.unwrap(), "/qux"); + assert_eq!(res.text().await, "/qux"); } #[tokio::test] @@ -180,17 +132,11 @@ async fn nested_url_original_extractor() { ), ); - let addr = run_in_background(app).await; + let client = TestClient::new(app); - let client = reqwest::Client::new(); - - let res = client - .get(format!("http://{}/foo/bar/baz", addr)) - .send() - .await - .unwrap(); + let res = client.get("/foo/bar/baz").send().await; assert_eq!(res.status(), StatusCode::OK); - assert_eq!(res.text().await.unwrap(), "/foo/bar/baz"); + assert_eq!(res.text().await, "/foo/bar/baz"); } #[tokio::test] @@ -209,17 +155,11 @@ async fn nested_service_sees_stripped_uri() { ), ); - let addr = run_in_background(app).await; + let client = TestClient::new(app); - let client = reqwest::Client::new(); - - let res = client - .get(format!("http://{}/foo/bar/baz", addr)) - .send() - .await - .unwrap(); + let res = client.get("/foo/bar/baz").send().await; assert_eq!(res.status(), StatusCode::OK); - assert_eq!(res.text().await.unwrap(), "/baz"); + assert_eq!(res.text().await, "/baz"); } #[tokio::test] @@ -234,14 +174,8 @@ async fn nest_static_file_server() { }), ); - let addr = run_in_background(app).await; + let client = TestClient::new(app); - let client = reqwest::Client::new(); - - let res = client - .get(format!("http://{}/static/README.md", addr)) - .send() - .await - .unwrap(); + let res = client.get("/static/README.md").send().await; assert_eq!(res.status(), StatusCode::OK); } diff --git a/src/tests/or.rs b/src/tests/or.rs index db62b26e..2c9a761e 100644 --- a/src/tests/or.rs +++ b/src/tests/or.rs @@ -11,36 +11,18 @@ async fn basic() { let two = Router::new().route("/baz", get(|| async {})); let app = one.or(two); - let addr = run_in_background(app).await; + let client = TestClient::new(app); - let client = reqwest::Client::new(); - - let res = client - .get(format!("http://{}/foo", addr)) - .send() - .await - .unwrap(); + let res = client.get("/foo").send().await; assert_eq!(res.status(), StatusCode::OK); - let res = client - .get(format!("http://{}/bar", addr)) - .send() - .await - .unwrap(); + let res = client.get("/bar").send().await; assert_eq!(res.status(), StatusCode::OK); - let res = client - .get(format!("http://{}/baz", addr)) - .send() - .await - .unwrap(); + let res = client.get("/baz").send().await; assert_eq!(res.status(), StatusCode::OK); - let res = client - .get(format!("http://{}/qux", addr)) - .send() - .await - .unwrap(); + let res = client.get("/qux").send().await; assert_eq!(res.status(), StatusCode::NOT_FOUND); } @@ -86,19 +68,13 @@ async fn multiple_ors_balanced_differently() { S::Future: Send, S::Error: Into, { - let addr = run_in_background(app).await; - - let client = reqwest::Client::new(); + let client = TestClient::new(app); for n in ["one", "two", "three", "four"].iter() { println!("running: {} / {}", name, n); - let res = client - .get(format!("http://{}/{}", addr, n)) - .send() - .await - .unwrap(); + let res = client.get(&format!("/{}", n)).send().await; assert_eq!(res.status(), StatusCode::OK); - assert_eq!(res.text().await.unwrap(), *n); + assert_eq!(res.text().await, *n); } } } @@ -110,22 +86,12 @@ async fn or_nested_inside_other_thing() { .or(Router::new().route("/baz", get(|| async {}))); let app = Router::new().nest("/foo", inner); - let addr = run_in_background(app).await; + let client = TestClient::new(app); - let client = reqwest::Client::new(); - - let res = client - .get(format!("http://{}/foo/bar", addr)) - .send() - .await - .unwrap(); + let res = client.get("/foo/bar").send().await; assert_eq!(res.status(), StatusCode::OK); - let res = client - .get(format!("http://{}/foo/baz", addr)) - .send() - .await - .unwrap(); + let res = client.get("/foo/baz").send().await; assert_eq!(res.status(), StatusCode::OK); } @@ -135,29 +101,15 @@ async fn or_with_route_following() { let two = Router::new().route("/two", get(|| async { "two" })); let app = one.or(two).route("/three", get(|| async { "three" })); - let addr = run_in_background(app).await; + let client = TestClient::new(app); - let client = reqwest::Client::new(); - - let res = client - .get(format!("http://{}/one", addr)) - .send() - .await - .unwrap(); + let res = client.get("/one").send().await; assert_eq!(res.status(), StatusCode::OK); - let res = client - .get(format!("http://{}/two", addr)) - .send() - .await - .unwrap(); + let res = client.get("/two").send().await; assert_eq!(res.status(), StatusCode::OK); - let res = client - .get(format!("http://{}/three", addr)) - .send() - .await - .unwrap(); + let res = client.get("/three").send().await; assert_eq!(res.status(), StatusCode::OK); } @@ -169,22 +121,12 @@ async fn layer() { .layer(ConcurrencyLimitLayer::new(10)); let app = one.or(two); - let addr = run_in_background(app).await; + let client = TestClient::new(app); - let client = reqwest::Client::new(); - - let res = client - .get(format!("http://{}/foo", addr)) - .send() - .await - .unwrap(); + let res = client.get("/foo").send().await; assert_eq!(res.status(), StatusCode::OK); - let res = client - .get(format!("http://{}/bar", addr)) - .send() - .await - .unwrap(); + let res = client.get("/bar").send().await; assert_eq!(res.status(), StatusCode::OK); } @@ -192,20 +134,14 @@ async fn layer() { async fn layer_and_handle_error() { let one = Router::new().route("/foo", get(|| async {})); let two = Router::new() - .route("/time-out", get(futures::future::pending::<()>)) + .route("/timeout", get(futures::future::pending::<()>)) .layer(TimeoutLayer::new(Duration::from_millis(10))) .handle_error(|_| Ok(StatusCode::REQUEST_TIMEOUT)); let app = one.or(two); - let addr = run_in_background(app).await; + let client = TestClient::new(app); - let client = reqwest::Client::new(); - - let res = client - .get(format!("http://{}/time-out", addr)) - .send() - .await - .unwrap(); + let res = client.get("/timeout").send().await; assert_eq!(res.status(), StatusCode::REQUEST_TIMEOUT); } @@ -215,15 +151,9 @@ async fn nesting() { let two = Router::new().nest("/bar", Router::new().route("/baz", get(|| async {}))); let app = one.or(two); - let addr = run_in_background(app).await; + let client = TestClient::new(app); - let client = reqwest::Client::new(); - - let res = client - .get(format!("http://{}/bar/baz", addr)) - .send() - .await - .unwrap(); + let res = client.get("/bar/baz").send().await; assert_eq!(res.status(), StatusCode::OK); } @@ -233,15 +163,9 @@ async fn boxed() { let two = Router::new().route("/bar", get(|| async {})).boxed(); let app = one.or(two); - let addr = run_in_background(app).await; + let client = TestClient::new(app); - let client = reqwest::Client::new(); - - let res = client - .get(format!("http://{}/bar", addr)) - .send() - .await - .unwrap(); + let res = client.get("/bar").send().await; assert_eq!(res.status(), StatusCode::OK); } @@ -256,24 +180,14 @@ async fn many_ors() { .or(Router::new().route("/r6", get(|| async {}))) .or(Router::new().route("/r7", get(|| async {}))); - let addr = run_in_background(app).await; - - let client = reqwest::Client::new(); + let client = TestClient::new(app); for n in 1..=7 { - let res = client - .get(format!("http://{}/r{}", addr, n)) - .send() - .await - .unwrap(); + let res = client.get(&format!("/r{}", n)).send().await; assert_eq!(res.status(), StatusCode::OK); } - let res = client - .get(format!("http://{}/r8", addr)) - .send() - .await - .unwrap(); + let res = client.get("/r8").send().await; assert_eq!(res.status(), StatusCode::NOT_FOUND); } @@ -293,22 +207,12 @@ async fn services() { })), )); - let addr = run_in_background(app).await; + let client = TestClient::new(app); - let client = reqwest::Client::new(); - - let res = client - .get(format!("http://{}/foo", addr)) - .send() - .await - .unwrap(); + let res = client.get("/foo").send().await; assert_eq!(res.status(), StatusCode::OK); - let res = client - .get(format!("http://{}/bar", addr)) - .send() - .await - .unwrap(); + let res = client.get("/bar").send().await; assert_eq!(res.status(), StatusCode::OK); } @@ -329,18 +233,12 @@ async fn nesting_and_seeing_the_right_uri() { let one = Router::new().nest("/foo", Router::new().route("/bar", get(all_the_uris))); let two = Router::new().route("/foo", get(all_the_uris)); - let addr = run_in_background(one.or(two)).await; + let client = TestClient::new(one.or(two)); - let client = reqwest::Client::new(); - - let res = client - .get(format!("http://{}/foo/bar", addr)) - .send() - .await - .unwrap(); + let res = client.get("/foo/bar").send().await; assert_eq!(res.status(), StatusCode::OK); assert_eq!( - res.json::().await.unwrap(), + res.json::().await, json!({ "uri": "/bar", "request_uri": "/bar", @@ -348,14 +246,10 @@ async fn nesting_and_seeing_the_right_uri() { }) ); - let res = client - .get(format!("http://{}/foo", addr)) - .send() - .await - .unwrap(); + let res = client.get("/foo").send().await; assert_eq!(res.status(), StatusCode::OK); assert_eq!( - res.json::().await.unwrap(), + res.json::().await, json!({ "uri": "/foo", "request_uri": "/foo", @@ -372,18 +266,12 @@ async fn nesting_and_seeing_the_right_uri_at_more_levels_of_nesting() { ); let two = Router::new().route("/foo", get(all_the_uris)); - let addr = run_in_background(one.or(two)).await; + let client = TestClient::new(one.or(two)); - let client = reqwest::Client::new(); - - let res = client - .get(format!("http://{}/foo/bar/baz", addr)) - .send() - .await - .unwrap(); + let res = client.get("/foo/bar/baz").send().await; assert_eq!(res.status(), StatusCode::OK); assert_eq!( - res.json::().await.unwrap(), + res.json::().await, json!({ "uri": "/baz", "request_uri": "/baz", @@ -391,14 +279,10 @@ async fn nesting_and_seeing_the_right_uri_at_more_levels_of_nesting() { }) ); - let res = client - .get(format!("http://{}/foo", addr)) - .send() - .await - .unwrap(); + let res = client.get("/foo").send().await; assert_eq!(res.status(), StatusCode::OK); assert_eq!( - res.json::().await.unwrap(), + res.json::().await, json!({ "uri": "/foo", "request_uri": "/foo", @@ -416,18 +300,12 @@ async fn nesting_and_seeing_the_right_uri_ors_with_nesting() { let two = Router::new().nest("/foo", Router::new().route("/qux", get(all_the_uris))); let three = Router::new().route("/foo", get(all_the_uris)); - let addr = run_in_background(one.or(two).or(three)).await; + let client = TestClient::new(one.or(two).or(three)); - let client = reqwest::Client::new(); - - let res = client - .get(format!("http://{}/foo/bar/baz", addr)) - .send() - .await - .unwrap(); + let res = client.get("/foo/bar/baz").send().await; assert_eq!(res.status(), StatusCode::OK); assert_eq!( - res.json::().await.unwrap(), + res.json::().await, json!({ "uri": "/baz", "request_uri": "/baz", @@ -435,14 +313,10 @@ async fn nesting_and_seeing_the_right_uri_ors_with_nesting() { }) ); - let res = client - .get(format!("http://{}/foo/qux", addr)) - .send() - .await - .unwrap(); + let res = client.get("/foo/qux").send().await; assert_eq!(res.status(), StatusCode::OK); assert_eq!( - res.json::().await.unwrap(), + res.json::().await, json!({ "uri": "/qux", "request_uri": "/qux", @@ -450,14 +324,10 @@ async fn nesting_and_seeing_the_right_uri_ors_with_nesting() { }) ); - let res = client - .get(format!("http://{}/foo", addr)) - .send() - .await - .unwrap(); + let res = client.get("/foo").send().await; assert_eq!(res.status(), StatusCode::OK); assert_eq!( - res.json::().await.unwrap(), + res.json::().await, json!({ "uri": "/foo", "request_uri": "/foo", @@ -474,18 +344,12 @@ async fn nesting_and_seeing_the_right_uri_ors_with_multi_segment_uris() { ); let two = Router::new().route("/foo/bar", get(all_the_uris)); - let addr = run_in_background(one.or(two)).await; + let client = TestClient::new(one.or(two)); - let client = reqwest::Client::new(); - - let res = client - .get(format!("http://{}/foo/bar/baz", addr)) - .send() - .await - .unwrap(); + let res = client.get("/foo/bar/baz").send().await; assert_eq!(res.status(), StatusCode::OK); assert_eq!( - res.json::().await.unwrap(), + res.json::().await, json!({ "uri": "/baz", "request_uri": "/baz", @@ -493,14 +357,10 @@ async fn nesting_and_seeing_the_right_uri_ors_with_multi_segment_uris() { }) ); - let res = client - .get(format!("http://{}/foo/bar", addr)) - .send() - .await - .unwrap(); + let res = client.get("/foo/bar").send().await; assert_eq!(res.status(), StatusCode::OK); assert_eq!( - res.json::().await.unwrap(), + res.json::().await, json!({ "uri": "/foo/bar", "request_uri": "/foo/bar",