mirror of
https://github.com/tokio-rs/axum.git
synced 2025-09-28 13:30:39 +00:00
Reorganize tests (#456)
* Reorganize tests This breaks up the large `crate::tests` module by moving some of the tests into a place that makes more sense. For example tests of JSON serialization are moved to the `crate::json` module. The remaining routing tests have been moved to `crate::routing::tests`. I generally prefer having tests close to the code they're testing. Makes it easier to see how/if something is tested. * Try pinning to older version of async-graphql * Revert "Try pinning to older version of async-graphql" This reverts commit 2e2cae7d12f5e433a16d6607497d587863f04384. * don't test examples on 1.54 on CI * move ci steps around a bit
This commit is contained in:
parent
420918a53a
commit
0f1f28062d
25
.github/workflows/CI.yml
vendored
25
.github/workflows/CI.yml
vendored
@ -64,12 +64,11 @@ jobs:
|
|||||||
run: cargo hack check --each-feature --no-dev-deps --all
|
run: cargo hack check --each-feature --no-dev-deps --all
|
||||||
|
|
||||||
test-versions:
|
test-versions:
|
||||||
# Test against the stable, beta, and nightly Rust toolchains on ubuntu-latest.
|
|
||||||
needs: check
|
needs: check
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
rust: [stable, beta, nightly, 1.54]
|
rust: [stable, beta, nightly]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@master
|
- uses: actions/checkout@master
|
||||||
- uses: actions-rs/toolchain@v1
|
- uses: actions-rs/toolchain@v1
|
||||||
@ -84,6 +83,28 @@ jobs:
|
|||||||
command: test
|
command: test
|
||||||
args: --all --all-features --all-targets
|
args: --all --all-features --all-targets
|
||||||
|
|
||||||
|
# some examples doesn't support 1.54 (such as async-graphql)
|
||||||
|
# so we only test axum itself on 1.54
|
||||||
|
test-msrv:
|
||||||
|
needs: check
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
rust: [1.54]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@master
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: ${{ matrix.rust }}
|
||||||
|
override: true
|
||||||
|
profile: minimal
|
||||||
|
- uses: Swatinem/rust-cache@v1
|
||||||
|
- name: Run tests
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: test
|
||||||
|
args: -p axum --all-features --all-targets
|
||||||
|
|
||||||
test-docs:
|
test-docs:
|
||||||
needs: check
|
needs: check
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
@ -132,7 +132,7 @@ fn stream_body_traits() {
|
|||||||
|
|
||||||
type EmptyStream = StreamBody<Empty<Result<Bytes, BoxError>>>;
|
type EmptyStream = StreamBody<Empty<Result<Bytes, BoxError>>>;
|
||||||
|
|
||||||
crate::tests::assert_send::<EmptyStream>();
|
crate::test_helpers::assert_send::<EmptyStream>();
|
||||||
crate::tests::assert_sync::<EmptyStream>();
|
crate::test_helpers::assert_sync::<EmptyStream>();
|
||||||
crate::tests::assert_unpin::<EmptyStream>();
|
crate::test_helpers::assert_unpin::<EmptyStream>();
|
||||||
}
|
}
|
||||||
|
@ -203,7 +203,7 @@ pub mod future {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn traits() {
|
fn traits() {
|
||||||
use crate::tests::*;
|
use crate::test_helpers::*;
|
||||||
|
|
||||||
assert_send::<HandleError<(), (), NotSendSync>>();
|
assert_send::<HandleError<(), (), NotSendSync>>();
|
||||||
assert_sync::<HandleError<(), (), NotSendSync>>();
|
assert_sync::<HandleError<(), (), NotSendSync>>();
|
||||||
|
@ -32,7 +32,7 @@ pub struct IntoMakeServiceWithConnectInfo<S, C> {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn traits() {
|
fn traits() {
|
||||||
use crate::tests::*;
|
use crate::test_helpers::*;
|
||||||
assert_send::<IntoMakeServiceWithConnectInfo<(), NotSendSync>>();
|
assert_send::<IntoMakeServiceWithConnectInfo<(), NotSendSync>>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,3 +74,60 @@ impl<T, const N: u64> Deref for ContentLengthLimit<T, N> {
|
|||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::{routing::post, test_helpers::*, Router};
|
||||||
|
use bytes::Bytes;
|
||||||
|
use http::StatusCode;
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn body_with_length_limit() {
|
||||||
|
use std::iter::repeat;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct Input {
|
||||||
|
foo: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
const LIMIT: u64 = 8;
|
||||||
|
|
||||||
|
let app = Router::new().route(
|
||||||
|
"/",
|
||||||
|
post(|_body: ContentLengthLimit<Bytes, LIMIT>| async {}),
|
||||||
|
);
|
||||||
|
|
||||||
|
let client = TestClient::new(app);
|
||||||
|
let res = client
|
||||||
|
.post("/")
|
||||||
|
.body(repeat(0_u8).take((LIMIT - 1) as usize).collect::<Vec<_>>())
|
||||||
|
.send()
|
||||||
|
.await;
|
||||||
|
assert_eq!(res.status(), StatusCode::OK);
|
||||||
|
|
||||||
|
let res = client
|
||||||
|
.post("/")
|
||||||
|
.body(repeat(0_u8).take(LIMIT as usize).collect::<Vec<_>>())
|
||||||
|
.send()
|
||||||
|
.await;
|
||||||
|
assert_eq!(res.status(), StatusCode::OK);
|
||||||
|
|
||||||
|
let res = client
|
||||||
|
.post("/")
|
||||||
|
.body(repeat(0_u8).take((LIMIT + 1) as usize).collect::<Vec<_>>())
|
||||||
|
.send()
|
||||||
|
.await;
|
||||||
|
assert_eq!(res.status(), StatusCode::PAYLOAD_TOO_LARGE);
|
||||||
|
|
||||||
|
let res = client
|
||||||
|
.post("/")
|
||||||
|
.body(reqwest::Body::wrap_stream(futures_util::stream::iter(
|
||||||
|
vec![Ok::<_, std::io::Error>(bytes::Bytes::new())],
|
||||||
|
)))
|
||||||
|
.send()
|
||||||
|
.await;
|
||||||
|
assert_eq!(res.status(), StatusCode::LENGTH_REQUIRED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -120,7 +120,7 @@ pub struct ExtractorMiddleware<S, E> {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn traits() {
|
fn traits() {
|
||||||
use crate::tests::*;
|
use crate::test_helpers::*;
|
||||||
assert_send::<ExtractorMiddleware<(), NotSendSync>>();
|
assert_send::<ExtractorMiddleware<(), NotSendSync>>();
|
||||||
assert_sync::<ExtractorMiddleware<(), NotSendSync>>();
|
assert_sync::<ExtractorMiddleware<(), NotSendSync>>();
|
||||||
}
|
}
|
||||||
@ -250,3 +250,57 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::{handler::Handler, routing::get, test_helpers::*, Router};
|
||||||
|
use http::StatusCode;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_extractor_middleware() {
|
||||||
|
struct RequireAuth;
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl<B> FromRequest<B> for RequireAuth
|
||||||
|
where
|
||||||
|
B: Send,
|
||||||
|
{
|
||||||
|
type Rejection = StatusCode;
|
||||||
|
|
||||||
|
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||||
|
if let Some(auth) = req
|
||||||
|
.headers()
|
||||||
|
.expect("headers already extracted")
|
||||||
|
.get("authorization")
|
||||||
|
.and_then(|v| v.to_str().ok())
|
||||||
|
{
|
||||||
|
if auth == "secret" {
|
||||||
|
return Ok(Self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(StatusCode::UNAUTHORIZED)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handler() {}
|
||||||
|
|
||||||
|
let app = Router::new().route(
|
||||||
|
"/",
|
||||||
|
get(handler.layer(extractor_middleware::<RequireAuth>())),
|
||||||
|
);
|
||||||
|
|
||||||
|
let client = TestClient::new(app);
|
||||||
|
|
||||||
|
let res = client.get("/").send().await;
|
||||||
|
assert_eq!(res.status(), StatusCode::UNAUTHORIZED);
|
||||||
|
|
||||||
|
let res = client
|
||||||
|
.get("/")
|
||||||
|
.header(http::header::AUTHORIZATION, "secret")
|
||||||
|
.send()
|
||||||
|
.await;
|
||||||
|
assert_eq!(res.status(), StatusCode::OK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -84,3 +84,100 @@ where
|
|||||||
Ok(matched_path)
|
Ok(matched_path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::{extract::Extension, handler::Handler, routing::get, test_helpers::*, Router};
|
||||||
|
use http::Request;
|
||||||
|
use std::task::{Context, Poll};
|
||||||
|
use tower_service::Service;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct SetMatchedPathExtension<S>(S);
|
||||||
|
|
||||||
|
impl<B, S> Service<Request<B>> for SetMatchedPathExtension<S>
|
||||||
|
where
|
||||||
|
S: Service<Request<B>>,
|
||||||
|
{
|
||||||
|
type Response = S::Response;
|
||||||
|
type Error = S::Error;
|
||||||
|
type Future = S::Future;
|
||||||
|
|
||||||
|
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
|
self.0.poll_ready(cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call(&mut self, mut req: Request<B>) -> Self::Future {
|
||||||
|
let path = req
|
||||||
|
.extensions()
|
||||||
|
.get::<MatchedPath>()
|
||||||
|
.unwrap()
|
||||||
|
.as_str()
|
||||||
|
.to_string();
|
||||||
|
req.extensions_mut().insert(MatchedPathFromMiddleware(path));
|
||||||
|
self.0.call(req)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct MatchedPathFromMiddleware(String);
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn access_matched_path() {
|
||||||
|
let api = Router::new().route(
|
||||||
|
"/users/:id",
|
||||||
|
get(|path: MatchedPath| async move { path.as_str().to_string() }),
|
||||||
|
);
|
||||||
|
|
||||||
|
async fn handler(
|
||||||
|
path: MatchedPath,
|
||||||
|
Extension(MatchedPathFromMiddleware(path_from_middleware)): Extension<
|
||||||
|
MatchedPathFromMiddleware,
|
||||||
|
>,
|
||||||
|
) -> String {
|
||||||
|
format!(
|
||||||
|
"extractor = {}, middleware = {}",
|
||||||
|
path.as_str(),
|
||||||
|
path_from_middleware
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let app = Router::new()
|
||||||
|
.route(
|
||||||
|
"/:key",
|
||||||
|
get(|path: MatchedPath| async move { path.as_str().to_string() }),
|
||||||
|
)
|
||||||
|
.nest("/api", api)
|
||||||
|
.nest(
|
||||||
|
"/public",
|
||||||
|
Router::new().route("/assets/*path", get(handler)),
|
||||||
|
)
|
||||||
|
.nest("/foo", handler.into_service())
|
||||||
|
.layer(tower::layer::layer_fn(SetMatchedPathExtension));
|
||||||
|
|
||||||
|
let client = TestClient::new(app);
|
||||||
|
|
||||||
|
let res = client.get("/foo").send().await;
|
||||||
|
assert_eq!(res.text().await, "/:key");
|
||||||
|
|
||||||
|
let res = client.get("/api/users/123").send().await;
|
||||||
|
assert_eq!(res.text().await, "/api/users/:id");
|
||||||
|
|
||||||
|
let res = client.get("/public/assets/css/style.css").send().await;
|
||||||
|
assert_eq!(
|
||||||
|
res.text().await,
|
||||||
|
"extractor = /public/assets/*path, middleware = /public/assets/*path"
|
||||||
|
);
|
||||||
|
|
||||||
|
let res = client.get("/foo/bar/baz").send().await;
|
||||||
|
assert_eq!(
|
||||||
|
res.text().await,
|
||||||
|
format!(
|
||||||
|
"extractor = /foo/*{}, middleware = /foo/*{}",
|
||||||
|
crate::routing::NEST_TAIL_PARAM,
|
||||||
|
crate::routing::NEST_TAIL_PARAM,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -356,3 +356,20 @@ pub(crate) fn has_content_type<B>(
|
|||||||
pub(crate) fn take_body<B>(req: &mut RequestParts<B>) -> Result<B, BodyAlreadyExtracted> {
|
pub(crate) fn take_body<B>(req: &mut RequestParts<B>) -> Result<B, BodyAlreadyExtracted> {
|
||||||
req.take_body().ok_or(BodyAlreadyExtracted)
|
req.take_body().ok_or(BodyAlreadyExtracted)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::test_helpers::*;
|
||||||
|
use crate::{routing::get, Router};
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn consume_body() {
|
||||||
|
let app = Router::new().route("/", get(|body: String| async { body }));
|
||||||
|
|
||||||
|
let client = TestClient::new(app);
|
||||||
|
let res = client.get("/").body("foo").send().await;
|
||||||
|
let body = res.text().await;
|
||||||
|
|
||||||
|
assert_eq!(body, "foo");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -173,11 +173,44 @@ where
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use http::StatusCode;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::tests::*;
|
use crate::test_helpers::*;
|
||||||
use crate::{routing::get, Router};
|
use crate::{routing::get, Router};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn extracting_url_params() {
|
||||||
|
let app = Router::new().route(
|
||||||
|
"/users/:id",
|
||||||
|
get(|Path(id): Path<i32>| async move {
|
||||||
|
assert_eq!(id, 42);
|
||||||
|
})
|
||||||
|
.post(|Path(params_map): Path<HashMap<String, i32>>| async move {
|
||||||
|
assert_eq!(params_map.get("id").unwrap(), &1337);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
let client = TestClient::new(app);
|
||||||
|
|
||||||
|
let res = client.get("/users/42").send().await;
|
||||||
|
assert_eq!(res.status(), StatusCode::OK);
|
||||||
|
|
||||||
|
let res = client.post("/users/1337").send().await;
|
||||||
|
assert_eq!(res.status(), StatusCode::OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn extracting_url_params_multiple_times() {
|
||||||
|
let app = Router::new().route("/users/:id", get(|_: Path<i32>, _: Path<String>| async {}));
|
||||||
|
|
||||||
|
let client = TestClient::new(app);
|
||||||
|
|
||||||
|
let res = client.get("/users/42").send().await;
|
||||||
|
assert_eq!(res.status(), StatusCode::OK);
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn percent_decoding() {
|
async fn percent_decoding() {
|
||||||
let app = Router::new().route(
|
let app = Router::new().route(
|
||||||
@ -235,4 +268,17 @@ mod tests {
|
|||||||
let res = client.get("/bar/baz/qux").send().await;
|
let res = client.get("/bar/baz/qux").send().await;
|
||||||
assert_eq!(res.text().await, "/baz/qux");
|
assert_eq!(res.text().await, "/baz/qux");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn captures_dont_match_empty_segments() {
|
||||||
|
let app = Router::new().route("/:key", get(|| async {}));
|
||||||
|
|
||||||
|
let client = TestClient::new(app);
|
||||||
|
|
||||||
|
let res = client.get("/").send().await;
|
||||||
|
assert_eq!(res.status(), StatusCode::NOT_FOUND);
|
||||||
|
|
||||||
|
let res = client.get("/foo").send().await;
|
||||||
|
assert_eq!(res.status(), StatusCode::OK);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -238,8 +238,8 @@ impl fmt::Debug for BodyStream {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn body_stream_traits() {
|
fn body_stream_traits() {
|
||||||
crate::tests::assert_send::<BodyStream>();
|
crate::test_helpers::assert_send::<BodyStream>();
|
||||||
crate::tests::assert_sync::<BodyStream>();
|
crate::test_helpers::assert_sync::<BodyStream>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extractor that extracts the raw request body.
|
/// Extractor that extracts the raw request body.
|
||||||
@ -326,7 +326,7 @@ where
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{body::Body, routing::post, tests::*, Router};
|
use crate::{body::Body, routing::post, test_helpers::*, Router};
|
||||||
use http::StatusCode;
|
use http::StatusCode;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
|
@ -140,7 +140,7 @@ impl std::error::Error for TypedHeaderRejection {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{response::IntoResponse, routing::get, tests::*, Router};
|
use crate::{response::IntoResponse, routing::get, test_helpers::*, Router};
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn typed_header() {
|
async fn typed_header() {
|
||||||
|
@ -19,7 +19,7 @@ pub struct IntoService<H, B, T> {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn traits() {
|
fn traits() {
|
||||||
use crate::tests::*;
|
use crate::test_helpers::*;
|
||||||
assert_send::<IntoService<(), NotSendSync, NotSendSync>>();
|
assert_send::<IntoService<(), NotSendSync, NotSendSync>>();
|
||||||
assert_sync::<IntoService<(), NotSendSync, NotSendSync>>();
|
assert_sync::<IntoService<(), NotSendSync, NotSendSync>>();
|
||||||
}
|
}
|
||||||
|
@ -384,10 +384,30 @@ impl<S, T> Layered<S, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[cfg(test)]
|
||||||
fn traits() {
|
mod tests {
|
||||||
use crate::routing::MethodRouter;
|
use super::*;
|
||||||
use crate::tests::*;
|
use crate::test_helpers::*;
|
||||||
assert_send::<MethodRouter<(), NotSendSync, NotSendSync, ()>>();
|
use http::StatusCode;
|
||||||
assert_sync::<MethodRouter<(), NotSendSync, NotSendSync, ()>>();
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn handler_into_service() {
|
||||||
|
async fn handle(body: String) -> impl IntoResponse {
|
||||||
|
format!("you said: {}", body)
|
||||||
|
}
|
||||||
|
|
||||||
|
let client = TestClient::new(handle.into_service());
|
||||||
|
|
||||||
|
let res = client.post("/").body("hi there!").send().await;
|
||||||
|
assert_eq!(res.status(), StatusCode::OK);
|
||||||
|
assert_eq!(res.text().await, "you said: hi there!");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn traits() {
|
||||||
|
use crate::routing::MethodRouter;
|
||||||
|
use crate::test_helpers::*;
|
||||||
|
assert_send::<MethodRouter<(), NotSendSync, NotSendSync, ()>>();
|
||||||
|
assert_sync::<MethodRouter<(), NotSendSync, NotSendSync, ()>>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
66
src/json.rs
66
src/json.rs
@ -195,3 +195,69 @@ where
|
|||||||
res
|
res
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::{routing::post, test_helpers::*, Router};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use serde_json::{json, Value};
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn deserialize_body() {
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct Input {
|
||||||
|
foo: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
let app = Router::new().route("/", post(|input: Json<Input>| async { input.0.foo }));
|
||||||
|
|
||||||
|
let client = TestClient::new(app);
|
||||||
|
let res = client.post("/").json(&json!({ "foo": "bar" })).send().await;
|
||||||
|
let body = res.text().await;
|
||||||
|
|
||||||
|
assert_eq!(body, "bar");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn consume_body_to_json_requires_json_content_type() {
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct Input {
|
||||||
|
foo: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
let app = Router::new().route("/", post(|input: Json<Input>| async { input.0.foo }));
|
||||||
|
|
||||||
|
let client = TestClient::new(app);
|
||||||
|
let res = client.post("/").body(r#"{ "foo": "bar" }"#).send().await;
|
||||||
|
|
||||||
|
let status = res.status();
|
||||||
|
dbg!(res.text().await);
|
||||||
|
|
||||||
|
assert_eq!(status, StatusCode::BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn json_content_types() {
|
||||||
|
async fn valid_json_content_type(content_type: &str) -> bool {
|
||||||
|
println!("testing {:?}", content_type);
|
||||||
|
|
||||||
|
let app = Router::new().route("/", post(|Json(_): Json<Value>| async {}));
|
||||||
|
|
||||||
|
let res = TestClient::new(app)
|
||||||
|
.post("/")
|
||||||
|
.header("content-type", content_type)
|
||||||
|
.body("{}")
|
||||||
|
.send()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
res.status() == StatusCode::OK
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(valid_json_content_type("application/json").await);
|
||||||
|
assert!(valid_json_content_type("application/json; charset=utf-8").await);
|
||||||
|
assert!(valid_json_content_type("application/json;charset=utf-8").await);
|
||||||
|
assert!(valid_json_content_type("application/cloudevents+json").await);
|
||||||
|
assert!(!valid_json_content_type("text/json").await);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -325,7 +325,7 @@ pub mod response;
|
|||||||
pub mod routing;
|
pub mod routing;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod test_helpers;
|
||||||
|
|
||||||
pub use add_extension::{AddExtension, AddExtensionLayer};
|
pub use add_extension::{AddExtension, AddExtensionLayer};
|
||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
|
@ -50,7 +50,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn traits() {
|
fn traits() {
|
||||||
use crate::tests::*;
|
use crate::test_helpers::*;
|
||||||
|
|
||||||
assert_send::<IntoMakeService<Body>>();
|
assert_send::<IntoMakeService<Body>>();
|
||||||
}
|
}
|
||||||
|
@ -74,7 +74,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn traits() {
|
fn traits() {
|
||||||
use crate::tests::*;
|
use crate::test_helpers::*;
|
||||||
|
|
||||||
assert_send::<MethodNotAllowed<NotSendSync>>();
|
assert_send::<MethodNotAllowed<NotSendSync>>();
|
||||||
assert_sync::<MethodNotAllowed<NotSendSync>>();
|
assert_sync::<MethodNotAllowed<NotSendSync>>();
|
||||||
|
@ -37,6 +37,9 @@ mod not_found;
|
|||||||
mod route;
|
mod route;
|
||||||
mod strip_prefix;
|
mod strip_prefix;
|
||||||
|
|
||||||
|
#[cfg(tests)]
|
||||||
|
mod tests;
|
||||||
|
|
||||||
pub use self::{
|
pub use self::{
|
||||||
into_make_service::IntoMakeService, method_filter::MethodFilter,
|
into_make_service::IntoMakeService, method_filter::MethodFilter,
|
||||||
method_not_allowed::MethodNotAllowed, route::Route,
|
method_not_allowed::MethodNotAllowed, route::Route,
|
||||||
@ -574,6 +577,6 @@ where
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn traits() {
|
fn traits() {
|
||||||
use crate::tests::*;
|
use crate::test_helpers::*;
|
||||||
assert_send::<Router<()>>();
|
assert_send::<Router<()>>();
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn traits() {
|
fn traits() {
|
||||||
use crate::tests::*;
|
use crate::test_helpers::*;
|
||||||
assert_send::<Route<()>>();
|
assert_send::<Route<()>>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -553,7 +553,7 @@ where
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn traits() {
|
fn traits() {
|
||||||
use crate::tests::*;
|
use crate::test_helpers::*;
|
||||||
|
|
||||||
assert_send::<MethodRouter<(), (), NotSendSync>>();
|
assert_send::<MethodRouter<(), (), NotSendSync>>();
|
||||||
assert_sync::<MethodRouter<(), (), NotSendSync>>();
|
assert_sync::<MethodRouter<(), (), NotSendSync>>();
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
#![allow(clippy::blacklisted_name)]
|
|
||||||
|
|
||||||
use crate::error_handling::HandleErrorLayer;
|
use crate::error_handling::HandleErrorLayer;
|
||||||
use crate::extract::{Extension, MatchedPath};
|
use crate::extract::{Extension, MatchedPath};
|
||||||
|
use crate::test_helpers::*;
|
||||||
use crate::BoxError;
|
use crate::BoxError;
|
||||||
use crate::{
|
use crate::{
|
||||||
extract::{self, Path},
|
extract::{self, Path},
|
||||||
@ -31,12 +30,9 @@ use tower::{service_fn, timeout::TimeoutLayer, ServiceBuilder, ServiceExt};
|
|||||||
use tower_http::auth::RequireAuthorizationLayer;
|
use tower_http::auth::RequireAuthorizationLayer;
|
||||||
use tower_service::Service;
|
use tower_service::Service;
|
||||||
|
|
||||||
pub(crate) use helpers::*;
|
|
||||||
|
|
||||||
mod fallback;
|
mod fallback;
|
||||||
mod get_to_head;
|
mod get_to_head;
|
||||||
mod handle_error;
|
mod handle_error;
|
||||||
mod helpers;
|
|
||||||
mod merge;
|
mod merge;
|
||||||
mod nest;
|
mod nest;
|
||||||
|
|
||||||
@ -73,105 +69,6 @@ async fn hello_world() {
|
|||||||
assert_eq!(body, "users#create");
|
assert_eq!(body, "users#create");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn consume_body() {
|
|
||||||
let app = Router::new().route("/", get(|body: String| async { body }));
|
|
||||||
|
|
||||||
let client = TestClient::new(app);
|
|
||||||
let res = client.get("/").body("foo").send().await;
|
|
||||||
let body = res.text().await;
|
|
||||||
|
|
||||||
assert_eq!(body, "foo");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn deserialize_body() {
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
struct Input {
|
|
||||||
foo: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
let app = Router::new().route(
|
|
||||||
"/",
|
|
||||||
post(|input: extract::Json<Input>| async { input.0.foo }),
|
|
||||||
);
|
|
||||||
|
|
||||||
let client = TestClient::new(app);
|
|
||||||
let res = client.post("/").json(&json!({ "foo": "bar" })).send().await;
|
|
||||||
let body = res.text().await;
|
|
||||||
|
|
||||||
assert_eq!(body, "bar");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn consume_body_to_json_requires_json_content_type() {
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
struct Input {
|
|
||||||
foo: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
let app = Router::new().route(
|
|
||||||
"/",
|
|
||||||
post(|input: extract::Json<Input>| async { input.0.foo }),
|
|
||||||
);
|
|
||||||
|
|
||||||
let client = TestClient::new(app);
|
|
||||||
let res = client.post("/").body(r#"{ "foo": "bar" }"#).send().await;
|
|
||||||
|
|
||||||
let status = res.status();
|
|
||||||
dbg!(res.text().await);
|
|
||||||
|
|
||||||
assert_eq!(status, StatusCode::BAD_REQUEST);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn body_with_length_limit() {
|
|
||||||
use std::iter::repeat;
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
struct Input {
|
|
||||||
foo: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
const LIMIT: u64 = 8;
|
|
||||||
|
|
||||||
let app = Router::new().route(
|
|
||||||
"/",
|
|
||||||
post(|_body: extract::ContentLengthLimit<Bytes, LIMIT>| async {}),
|
|
||||||
);
|
|
||||||
|
|
||||||
let client = TestClient::new(app);
|
|
||||||
let res = client
|
|
||||||
.post("/")
|
|
||||||
.body(repeat(0_u8).take((LIMIT - 1) as usize).collect::<Vec<_>>())
|
|
||||||
.send()
|
|
||||||
.await;
|
|
||||||
assert_eq!(res.status(), StatusCode::OK);
|
|
||||||
|
|
||||||
let res = client
|
|
||||||
.post("/")
|
|
||||||
.body(repeat(0_u8).take(LIMIT as usize).collect::<Vec<_>>())
|
|
||||||
.send()
|
|
||||||
.await;
|
|
||||||
assert_eq!(res.status(), StatusCode::OK);
|
|
||||||
|
|
||||||
let res = client
|
|
||||||
.post("/")
|
|
||||||
.body(repeat(0_u8).take((LIMIT + 1) as usize).collect::<Vec<_>>())
|
|
||||||
.send()
|
|
||||||
.await;
|
|
||||||
assert_eq!(res.status(), StatusCode::PAYLOAD_TOO_LARGE);
|
|
||||||
|
|
||||||
let res = client
|
|
||||||
.post("/")
|
|
||||||
.body(reqwest::Body::wrap_stream(futures_util::stream::iter(
|
|
||||||
vec![Ok::<_, std::io::Error>(bytes::Bytes::new())],
|
|
||||||
)))
|
|
||||||
.send()
|
|
||||||
.await;
|
|
||||||
assert_eq!(res.status(), StatusCode::LENGTH_REQUIRED);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn routing() {
|
async fn routing() {
|
||||||
let app = Router::new()
|
let app = Router::new()
|
||||||
@ -209,42 +106,8 @@ async fn routing() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn extracting_url_params() {
|
async fn router_type_doesnt_change() {
|
||||||
let app = Router::new().route(
|
let app: Router = Router::new()
|
||||||
"/users/:id",
|
|
||||||
get(|Path(id): Path<i32>| async move {
|
|
||||||
assert_eq!(id, 42);
|
|
||||||
})
|
|
||||||
.post(|Path(params_map): Path<HashMap<String, i32>>| async move {
|
|
||||||
assert_eq!(params_map.get("id").unwrap(), &1337);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
let client = TestClient::new(app);
|
|
||||||
|
|
||||||
let res = client.get("/users/42").send().await;
|
|
||||||
assert_eq!(res.status(), StatusCode::OK);
|
|
||||||
|
|
||||||
let res = client.post("/users/1337").send().await;
|
|
||||||
assert_eq!(res.status(), StatusCode::OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn extracting_url_params_multiple_times() {
|
|
||||||
let app = Router::new().route(
|
|
||||||
"/users/:id",
|
|
||||||
get(|_: extract::Path<i32>, _: extract::Path<String>| async {}),
|
|
||||||
);
|
|
||||||
|
|
||||||
let client = TestClient::new(app);
|
|
||||||
|
|
||||||
let res = client.get("/users/42").send().await;
|
|
||||||
assert_eq!(res.status(), StatusCode::OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn boxing() {
|
|
||||||
let app = Router::new()
|
|
||||||
.route(
|
.route(
|
||||||
"/",
|
"/",
|
||||||
on(MethodFilter::GET, |_: Request<Body>| async {
|
on(MethodFilter::GET, |_: Request<Body>| async {
|
||||||
@ -351,49 +214,6 @@ async fn service_in_bottom() {
|
|||||||
TestClient::new(app);
|
TestClient::new(app);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_extractor_middleware() {
|
|
||||||
struct RequireAuth;
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
impl<B> extract::FromRequest<B> for RequireAuth
|
|
||||||
where
|
|
||||||
B: Send,
|
|
||||||
{
|
|
||||||
type Rejection = StatusCode;
|
|
||||||
|
|
||||||
async fn from_request(req: &mut extract::RequestParts<B>) -> Result<Self, Self::Rejection> {
|
|
||||||
if let Some(auth) = req
|
|
||||||
.headers()
|
|
||||||
.expect("headers already extracted")
|
|
||||||
.get("authorization")
|
|
||||||
.and_then(|v| v.to_str().ok())
|
|
||||||
{
|
|
||||||
if auth == "secret" {
|
|
||||||
return Ok(Self);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(StatusCode::UNAUTHORIZED)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn handler() {}
|
|
||||||
|
|
||||||
let app = Router::new().route(
|
|
||||||
"/",
|
|
||||||
get(handler.layer(extract::extractor_middleware::<RequireAuth>())),
|
|
||||||
);
|
|
||||||
|
|
||||||
let client = TestClient::new(app);
|
|
||||||
|
|
||||||
let res = client.get("/").send().await;
|
|
||||||
assert_eq!(res.status(), StatusCode::UNAUTHORIZED);
|
|
||||||
|
|
||||||
let res = client.get("/").header(AUTHORIZATION, "secret").send().await;
|
|
||||||
assert_eq!(res.status(), StatusCode::OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn wrong_method_handler() {
|
async fn wrong_method_handler() {
|
||||||
let app = Router::new()
|
let app = Router::new()
|
||||||
@ -470,56 +290,6 @@ async fn multiple_methods_for_one_handler() {
|
|||||||
assert_eq!(res.status(), StatusCode::OK);
|
assert_eq!(res.status(), StatusCode::OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn handler_into_service() {
|
|
||||||
async fn handle(body: String) -> impl IntoResponse {
|
|
||||||
format!("you said: {}", body)
|
|
||||||
}
|
|
||||||
|
|
||||||
let client = TestClient::new(handle.into_service());
|
|
||||||
|
|
||||||
let res = client.post("/").body("hi there!").send().await;
|
|
||||||
assert_eq!(res.status(), StatusCode::OK);
|
|
||||||
assert_eq!(res.text().await, "you said: hi there!");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn captures_dont_match_empty_segments() {
|
|
||||||
let app = Router::new().route("/:key", get(|| async {}));
|
|
||||||
|
|
||||||
let client = TestClient::new(app);
|
|
||||||
|
|
||||||
let res = client.get("/").send().await;
|
|
||||||
assert_eq!(res.status(), StatusCode::NOT_FOUND);
|
|
||||||
|
|
||||||
let res = client.get("/foo").send().await;
|
|
||||||
assert_eq!(res.status(), StatusCode::OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn json_content_types() {
|
|
||||||
async fn valid_json_content_type(content_type: &str) -> bool {
|
|
||||||
println!("testing {:?}", content_type);
|
|
||||||
|
|
||||||
let app = Router::new().route("/", post(|Json(_): Json<Value>| async {}));
|
|
||||||
|
|
||||||
let res = TestClient::new(app)
|
|
||||||
.post("/")
|
|
||||||
.header("content-type", content_type)
|
|
||||||
.body("{}")
|
|
||||||
.send()
|
|
||||||
.await;
|
|
||||||
|
|
||||||
res.status() == StatusCode::OK
|
|
||||||
}
|
|
||||||
|
|
||||||
assert!(valid_json_content_type("application/json").await);
|
|
||||||
assert!(valid_json_content_type("application/json; charset=utf-8").await);
|
|
||||||
assert!(valid_json_content_type("application/json;charset=utf-8").await);
|
|
||||||
assert!(valid_json_content_type("application/cloudevents+json").await);
|
|
||||||
assert!(!valid_json_content_type("text/json").await);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn wildcard_sees_whole_url() {
|
async fn wildcard_sees_whole_url() {
|
||||||
let app = Router::new().route("/api/*rest", get(|uri: Uri| async move { uri.to_string() }));
|
let app = Router::new().route("/api/*rest", get(|uri: Uri| async move { uri.to_string() }));
|
||||||
@ -634,94 +404,6 @@ async fn wildcard_with_trailing_slash() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
struct SetMatchedPathExtension<S>(S);
|
|
||||||
|
|
||||||
impl<B, S> Service<Request<B>> for SetMatchedPathExtension<S>
|
|
||||||
where
|
|
||||||
S: Service<Request<B>>,
|
|
||||||
{
|
|
||||||
type Response = S::Response;
|
|
||||||
type Error = S::Error;
|
|
||||||
type Future = S::Future;
|
|
||||||
|
|
||||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
|
||||||
self.0.poll_ready(cx)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn call(&mut self, mut req: Request<B>) -> Self::Future {
|
|
||||||
let path = req
|
|
||||||
.extensions()
|
|
||||||
.get::<MatchedPath>()
|
|
||||||
.unwrap()
|
|
||||||
.as_str()
|
|
||||||
.to_string();
|
|
||||||
req.extensions_mut().insert(MatchedPathFromMiddleware(path));
|
|
||||||
self.0.call(req)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
struct MatchedPathFromMiddleware(String);
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn access_matched_path() {
|
|
||||||
let api = Router::new().route(
|
|
||||||
"/users/:id",
|
|
||||||
get(|path: MatchedPath| async move { path.as_str().to_string() }),
|
|
||||||
);
|
|
||||||
|
|
||||||
async fn handler(
|
|
||||||
path: MatchedPath,
|
|
||||||
Extension(MatchedPathFromMiddleware(path_from_middleware)): Extension<
|
|
||||||
MatchedPathFromMiddleware,
|
|
||||||
>,
|
|
||||||
) -> String {
|
|
||||||
format!(
|
|
||||||
"extractor = {}, middleware = {}",
|
|
||||||
path.as_str(),
|
|
||||||
path_from_middleware
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
let app = Router::new()
|
|
||||||
.route(
|
|
||||||
"/:key",
|
|
||||||
get(|path: MatchedPath| async move { path.as_str().to_string() }),
|
|
||||||
)
|
|
||||||
.nest("/api", api)
|
|
||||||
.nest(
|
|
||||||
"/public",
|
|
||||||
Router::new().route("/assets/*path", get(handler)),
|
|
||||||
)
|
|
||||||
.nest("/foo", handler.into_service())
|
|
||||||
.layer(tower::layer::layer_fn(SetMatchedPathExtension));
|
|
||||||
|
|
||||||
let client = TestClient::new(app);
|
|
||||||
|
|
||||||
let res = client.get("/foo").send().await;
|
|
||||||
assert_eq!(res.text().await, "/:key");
|
|
||||||
|
|
||||||
let res = client.get("/api/users/123").send().await;
|
|
||||||
assert_eq!(res.text().await, "/api/users/:id");
|
|
||||||
|
|
||||||
let res = client.get("/public/assets/css/style.css").send().await;
|
|
||||||
assert_eq!(
|
|
||||||
res.text().await,
|
|
||||||
"extractor = /public/assets/*path, middleware = /public/assets/*path"
|
|
||||||
);
|
|
||||||
|
|
||||||
let res = client.get("/foo/bar/baz").send().await;
|
|
||||||
assert_eq!(
|
|
||||||
res.text().await,
|
|
||||||
format!(
|
|
||||||
"extractor = /foo/*{}, middleware = /foo/*{}",
|
|
||||||
crate::routing::NEST_TAIL_PARAM,
|
|
||||||
crate::routing::NEST_TAIL_PARAM,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn static_and_dynamic_paths() {
|
async fn static_and_dynamic_paths() {
|
||||||
let app = Router::new()
|
let app = Router::new()
|
||||||
@ -801,9 +483,3 @@ async fn middleware_still_run_for_unmatched_requests() {
|
|||||||
async fn routing_to_router_panics() {
|
async fn routing_to_router_panics() {
|
||||||
TestClient::new(Router::new().route("/", Router::new()));
|
TestClient::new(Router::new().route("/", Router::new()));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn assert_send<T: Send>() {}
|
|
||||||
pub(crate) fn assert_sync<T: Sync>() {}
|
|
||||||
pub(crate) fn assert_unpin<T: Unpin>() {}
|
|
||||||
|
|
||||||
pub(crate) struct NotSendSync(*const ());
|
|
@ -1,16 +1,22 @@
|
|||||||
|
#![allow(clippy::blacklisted_name)]
|
||||||
|
|
||||||
use crate::BoxError;
|
use crate::BoxError;
|
||||||
use http::{
|
use http::{
|
||||||
header::{HeaderName, HeaderValue},
|
header::{HeaderName, HeaderValue},
|
||||||
Request, StatusCode,
|
Request, StatusCode,
|
||||||
};
|
};
|
||||||
use hyper::{Body, Server};
|
use hyper::{Body, Server};
|
||||||
use std::{
|
use std::net::SocketAddr;
|
||||||
convert::TryFrom,
|
use std::{convert::TryFrom, net::TcpListener};
|
||||||
net::{SocketAddr, TcpListener},
|
|
||||||
};
|
|
||||||
use tower::make::Shared;
|
use tower::make::Shared;
|
||||||
use tower_service::Service;
|
use tower_service::Service;
|
||||||
|
|
||||||
|
pub(crate) fn assert_send<T: Send>() {}
|
||||||
|
pub(crate) fn assert_sync<T: Sync>() {}
|
||||||
|
pub(crate) fn assert_unpin<T: Unpin>() {}
|
||||||
|
|
||||||
|
pub(crate) struct NotSendSync(*const ());
|
||||||
|
|
||||||
pub(crate) struct TestClient {
|
pub(crate) struct TestClient {
|
||||||
client: reqwest::Client,
|
client: reqwest::Client,
|
||||||
addr: SocketAddr,
|
addr: SocketAddr,
|
||||||
@ -53,12 +59,14 @@ impl TestClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub(crate) fn put(&self, url: &str) -> RequestBuilder {
|
pub(crate) fn put(&self, url: &str) -> RequestBuilder {
|
||||||
RequestBuilder {
|
RequestBuilder {
|
||||||
builder: self.client.put(format!("http://{}{}", self.addr, url)),
|
builder: self.client.put(format!("http://{}{}", self.addr, url)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub(crate) fn patch(&self, url: &str) -> RequestBuilder {
|
pub(crate) fn patch(&self, url: &str) -> RequestBuilder {
|
||||||
RequestBuilder {
|
RequestBuilder {
|
||||||
builder: self.client.patch(format!("http://{}{}", self.addr, url)),
|
builder: self.client.patch(format!("http://{}{}", self.addr, url)),
|
||||||
@ -71,8 +79,8 @@ pub(crate) struct RequestBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl RequestBuilder {
|
impl RequestBuilder {
|
||||||
pub(crate) async fn send(self) -> Response {
|
pub(crate) async fn send(self) -> TestResponse {
|
||||||
Response {
|
TestResponse {
|
||||||
response: self.builder.send().await.unwrap(),
|
response: self.builder.send().await.unwrap(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -101,15 +109,16 @@ impl RequestBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct Response {
|
pub(crate) struct TestResponse {
|
||||||
response: reqwest::Response,
|
response: reqwest::Response,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Response {
|
impl TestResponse {
|
||||||
pub(crate) async fn text(self) -> String {
|
pub(crate) async fn text(self) -> String {
|
||||||
self.response.text().await.unwrap()
|
self.response.text().await.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub(crate) async fn json<T>(self) -> T
|
pub(crate) async fn json<T>(self) -> T
|
||||||
where
|
where
|
||||||
T: serde::de::DeserializeOwned,
|
T: serde::de::DeserializeOwned,
|
Loading…
x
Reference in New Issue
Block a user