add tests for WithRejection<Valid<...>>

This commit is contained in:
gengteng
2023-08-05 11:57:09 +08:00
parent 58d48b66b3
commit 7134197a55
3 changed files with 107 additions and 17 deletions

View File

@@ -30,6 +30,7 @@ impl<T: Validate, R> HasValidate for WithRejection<T, R> {
#[cfg(test)]
mod tests {
use crate::tests::{Rejection, ValidTest};
use crate::Valid;
use axum::http::StatusCode;
use axum_extra::extract::{Cached, WithRejection};
use reqwest::RequestBuilder;
@@ -52,7 +53,6 @@ mod tests {
}
impl<T: ValidTest, R: Rejection> ValidTest for WithRejection<T, R> {
// just use conflict to test
const ERROR_STATUS_CODE: StatusCode = R::STATUS_CODE;
fn set_valid_request(builder: RequestBuilder) -> RequestBuilder {
@@ -68,4 +68,28 @@ mod tests {
T::set_invalid_request(builder)
}
}
impl<T: ValidTest, R> ValidTest for WithRejection<Valid<T>, R> {
// just use `418 I'm a teapot` to test
const ERROR_STATUS_CODE: StatusCode = StatusCode::IM_A_TEAPOT;
// If `WithRejection` is the outermost extractor,
// the error code returned will always be the one provided by WithRejection.
const INVALID_STATUS_CODE: StatusCode = StatusCode::IM_A_TEAPOT;
// If `WithRejection` is the outermost extractor,
// the returned body may not be in JSON format.
const JSON_SERIALIZABLE: bool = false;
fn set_valid_request(builder: RequestBuilder) -> RequestBuilder {
T::set_valid_request(builder)
}
fn set_error_request(builder: RequestBuilder) -> RequestBuilder {
// invalid requests will cause the Valid extractor to fail.
T::set_invalid_request(builder)
}
fn set_invalid_request(builder: RequestBuilder) -> RequestBuilder {
T::set_invalid_request(builder)
}
}
}

View File

@@ -155,8 +155,13 @@ pub mod tests {
/// 3. For an invalid request according to Valid, the server should return VALIDATION_ERROR_STATUS as the error code.
///
pub trait ValidTest {
/// Http status code when inner extractor failed
/// The HTTP status code returned when inner extractor failed.
const ERROR_STATUS_CODE: StatusCode;
/// The HTTP status code returned when the outer extractor fails.
/// Use crate::VALIDATION_ERROR_STATUS by default.
const INVALID_STATUS_CODE: StatusCode = crate::VALIDATION_ERROR_STATUS;
/// If the response body can be serialized into JSON format
const JSON_SERIALIZABLE: bool = true;
/// Build a valid request, the server should return `200 OK`.
fn set_valid_request(builder: RequestBuilder) -> RequestBuilder;
/// Build an invalid request according to the extractor, the server should return `Self::ERROR_STATUS_CODE`

View File

@@ -1,5 +1,6 @@
use crate::test::extra::{ParametersRejection, WithRejectionValidRejection};
use crate::tests::{ValidTest, ValidTestParameter};
use crate::{Valid, VALIDATION_ERROR_STATUS};
use crate::{HasValidate, Valid, VALIDATION_ERROR_STATUS};
use axum::extract::{Path, Query};
use axum::routing::{get, post};
use axum::{Form, Json, Router};
@@ -47,6 +48,14 @@ impl ValidTestParameter for Parameters {
}
}
impl HasValidate for Parameters {
type Validate = Parameters;
fn get_validate(&self) -> &Self::Validate {
self
}
}
#[tokio::test]
async fn test_main() -> anyhow::Result<()> {
let router = Router::new()
@@ -67,6 +76,10 @@ async fn test_main() -> anyhow::Result<()> {
.route(
extra::route::WITH_REJECTION,
post(extra::extract_with_rejection),
)
.route(
extra::route::WITH_REJECTION_VALID,
post(extra::extract_with_rejection_valid),
);
#[cfg(feature = "extra_query")]
@@ -182,16 +195,22 @@ async fn test_main() -> anyhow::Result<()> {
#[cfg(feature = "extra")]
{
use axum_extra::extract::{Cached, WithRejection};
use extra::TestRejection;
use extra::ValidWithRejectionRejection;
test_executor
.execute::<Cached<Parameters>>(Method::POST, extra::route::CACHED)
.await?;
test_executor
.execute::<WithRejection<Parameters, TestRejection>>(
.execute::<WithRejection<Parameters, ValidWithRejectionRejection>>(
Method::POST,
extra::route::WITH_REJECTION,
)
.await?;
test_executor
.execute::<WithRejection<Valid<Parameters>, WithRejectionValidRejection<ParametersRejection>>>(
Method::POST,
extra::route::WITH_REJECTION_VALID,
)
.await?;
}
#[cfg(feature = "extra_query")]
@@ -290,12 +309,15 @@ impl TestExecutor {
let invalid_response = T::set_invalid_request(invalid_builder).send().await?;
assert_eq!(
invalid_response.status(),
VALIDATION_ERROR_STATUS,
T::INVALID_STATUS_CODE,
"Invalid '{}' test failed.",
type_name
);
#[cfg(feature = "into_json")]
if T::JSON_SERIALIZABLE {
check_json(type_name, invalid_response).await;
}
println!("All '{}' tests passed.", type_name);
Ok(())
@@ -424,7 +446,7 @@ mod typed_header {
mod extra {
use crate::test::{validate_again, Parameters};
use crate::tests::{Rejection, ValidTest, ValidTestParameter};
use crate::Valid;
use crate::{Valid, ValidRejection};
use axum::extract::FromRequestParts;
use axum::http::request::Parts;
use axum::http::StatusCode;
@@ -435,6 +457,7 @@ mod extra {
pub mod route {
pub const CACHED: &str = "/cached";
pub const WITH_REJECTION: &str = "/with_rejection";
pub const WITH_REJECTION_VALID: &str = "/with_rejection_valid";
}
pub const PARAMETERS_HEADER: &str = "parameters-header";
pub const CACHED_REJECTION_STATUS: StatusCode = StatusCode::FORBIDDEN;
@@ -503,25 +526,27 @@ mod extra {
}
}
pub struct TestRejection {
_inner: ParametersRejection,
pub struct ValidWithRejectionRejection {
inner: ParametersRejection,
}
impl Rejection for TestRejection {
impl Rejection for ValidWithRejectionRejection {
const STATUS_CODE: StatusCode = StatusCode::CONFLICT;
}
impl IntoResponse for TestRejection {
impl IntoResponse for ValidWithRejectionRejection {
fn into_response(self) -> Response {
Self::STATUS_CODE.into_response()
let mut response = self.inner.into_response();
*response.status_mut() = Self::STATUS_CODE;
response
}
}
// satisfy the `WithRejection`'s extractor trait bound
// R: From<E::Rejection> + IntoResponse
impl From<ParametersRejection> for TestRejection {
fn from(_inner: ParametersRejection) -> Self {
Self { _inner }
impl From<ParametersRejection> for ValidWithRejectionRejection {
fn from(inner: ParametersRejection) -> Self {
Self { inner }
}
}
@@ -532,7 +557,43 @@ mod extra {
}
pub async fn extract_with_rejection(
Valid(WithRejection(parameters, _)): Valid<WithRejection<Parameters, TestRejection>>,
Valid(WithRejection(parameters, _)): Valid<
WithRejection<Parameters, ValidWithRejectionRejection>,
>,
) -> StatusCode {
validate_again(parameters)
}
pub struct WithRejectionValidRejection<E> {
inner: ValidRejection<E>,
}
impl<E> From<ValidRejection<E>> for WithRejectionValidRejection<E> {
fn from(inner: ValidRejection<E>) -> Self {
Self { inner }
}
}
impl<E: IntoResponse> IntoResponse for WithRejectionValidRejection<E> {
fn into_response(self) -> Response {
match self.inner {
ValidRejection::Valid(v) => {
(StatusCode::IM_A_TEAPOT, v.to_string()).into_response()
}
ValidRejection::Inner(i) => {
let mut res = i.into_response();
*res.status_mut() = StatusCode::IM_A_TEAPOT;
res
}
}
}
}
pub async fn extract_with_rejection_valid(
WithRejection(Valid(parameters), _): WithRejection<
Valid<Parameters>,
WithRejectionValidRejection<ParametersRejection>,
>,
) -> StatusCode {
validate_again(parameters)
}