From 2f1aa1060f5a56073a5a076995da9751cde9df8a Mon Sep 17 00:00:00 2001 From: gengteng Date: Sat, 7 Oct 2023 21:11:26 +0800 Subject: [PATCH] add test for Garde --- src/extra.rs | 6 ++-- src/extra/form.rs | 4 +-- src/extra/protobuf.rs | 4 +-- src/extra/query.rs | 4 +-- src/extra/typed_path.rs | 5 ++++ src/form.rs | 4 +-- src/garde.rs | 65 ++++++++++++----------------------------- src/json.rs | 4 +-- src/lib.rs | 2 +- src/msgpack.rs | 6 ++-- src/path.rs | 4 +-- src/query.rs | 4 +-- src/test.rs | 64 ++++++++++++++++++++++++++++++++++++++-- src/typed_header.rs | 4 +-- src/typed_multipart.rs | 6 ++-- src/yaml.rs | 4 +-- 16 files changed, 112 insertions(+), 78 deletions(-) diff --git a/src/extra.rs b/src/extra.rs index 82b1f68..640e6f1 100644 --- a/src/extra.rs +++ b/src/extra.rs @@ -238,9 +238,9 @@ pub mod typed_path; use crate::{HasValidate, HasValidateArgs}; use axum_extra::extract::{Cached, WithRejection}; -use validator::{Validate, ValidateArgs}; +use validator::ValidateArgs; -impl HasValidate for Cached { +impl HasValidate for Cached { type Validate = T; fn get_validate(&self) -> &Self::Validate { @@ -255,7 +255,7 @@ impl<'v, T: ValidateArgs<'v>> HasValidateArgs<'v> for Cached { } } -impl HasValidate for WithRejection { +impl HasValidate for WithRejection { type Validate = T; fn get_validate(&self) -> &T { &self.0 diff --git a/src/extra/form.rs b/src/extra/form.rs index 459f2b4..57c470f 100644 --- a/src/extra/form.rs +++ b/src/extra/form.rs @@ -40,9 +40,9 @@ use crate::{HasValidate, HasValidateArgs}; use axum_extra::extract::Form; -use validator::{Validate, ValidateArgs}; +use validator::ValidateArgs; -impl HasValidate for Form { +impl HasValidate for Form { type Validate = T; fn get_validate(&self) -> &T { &self.0 diff --git a/src/extra/protobuf.rs b/src/extra/protobuf.rs index d03f8fc..90a6d62 100644 --- a/src/extra/protobuf.rs +++ b/src/extra/protobuf.rs @@ -42,9 +42,9 @@ use crate::{HasValidate, HasValidateArgs}; use axum_extra::protobuf::Protobuf; -use validator::{Validate, ValidateArgs}; +use validator::ValidateArgs; -impl HasValidate for Protobuf { +impl HasValidate for Protobuf { type Validate = T; fn get_validate(&self) -> &T { &self.0 diff --git a/src/extra/query.rs b/src/extra/query.rs index c67b20e..99b5376 100644 --- a/src/extra/query.rs +++ b/src/extra/query.rs @@ -43,9 +43,9 @@ use crate::{HasValidate, HasValidateArgs}; use axum_extra::extract::Query; -use validator::{Validate, ValidateArgs}; +use validator::ValidateArgs; -impl HasValidate for Query { +impl HasValidate for Query { type Validate = T; fn get_validate(&self) -> &T { &self.0 diff --git a/src/extra/typed_path.rs b/src/extra/typed_path.rs index f0d5edf..2e26d73 100644 --- a/src/extra/typed_path.rs +++ b/src/extra/typed_path.rs @@ -49,6 +49,7 @@ //! } //! ``` +use crate::garde::Garde; use crate::{Valid, ValidEx}; use axum_extra::routing::TypedPath; use std::fmt::Display; @@ -60,3 +61,7 @@ impl TypedPath for Valid { impl TypedPath for ValidEx { const PATH: &'static str = T::PATH; } + +impl TypedPath for Garde { + const PATH: &'static str = T::PATH; +} diff --git a/src/form.rs b/src/form.rs index bd1df34..8810c4b 100644 --- a/src/form.rs +++ b/src/form.rs @@ -40,9 +40,9 @@ use crate::{HasValidate, HasValidateArgs}; use axum::Form; -use validator::{Validate, ValidateArgs}; +use validator::ValidateArgs; -impl HasValidate for Form { +impl HasValidate for Form { type Validate = T; fn get_validate(&self) -> &T { &self.0 diff --git a/src/garde.rs b/src/garde.rs index d636574..a801d67 100644 --- a/src/garde.rs +++ b/src/garde.rs @@ -14,9 +14,9 @@ use std::ops::{Deref, DerefMut}; /// # `Garde` data extractor /// #[derive(Debug, Clone, Copy, Default)] -pub struct Garde(pub E, pub A); +pub struct Garde(pub E); -impl Deref for Garde { +impl Deref for Garde { type Target = E; fn deref(&self) -> &Self::Target { @@ -24,19 +24,19 @@ impl Deref for Garde { } } -impl DerefMut for Garde { +impl DerefMut for Garde { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } -impl Display for Garde { +impl Display for Garde { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { self.0.fmt(f) } } -impl Garde { +impl Garde { /// Consumes the `ValidEx` and returns the validated data within. /// /// This returns the `E` type which represents the data that has been @@ -44,18 +44,6 @@ impl Garde { pub fn into_inner(self) -> E { self.0 } - - /// Returns a reference to the validation arguments. - /// - /// This provides access to the `A` type which contains the arguments used - /// to validate the data. These arguments were passed to the validation - /// function. - pub fn arguments<'a>(&'a self) -> <::T as Validate>::Context - where - A: GardeArgument, - { - self.1.get() - } } fn response_builder(ve: garde::Report) -> Response { @@ -69,23 +57,6 @@ fn response_builder(ve: garde::Report) -> Response { } } -/// `Arguments` provides the validation arguments for the data type `T`. -/// -/// This trait has an associated type `T` which represents the data type to -/// validate. `T` must implement the `ValidateArgs` trait which defines the -/// validation logic. -/// -/// It's important to mention that types implementing `Arguments` should be a part of the router's state -/// (either through implementing `FromRef` or by directly becoming the state) -/// to enable automatic arguments retrieval during validation. -/// -pub trait GardeArgument { - /// The data type to validate using this arguments - type T: Validate; - /// This method gets the arguments required by `ValidateArgs::validate_args` - fn get(&self) -> <::T as Validate>::Context; -} - /// `ValidRejection` is returned when the `Valid` extractor fails. /// /// This enumeration captures two types of errors that can occur when using `Valid`: errors related to the validation @@ -93,7 +64,7 @@ pub trait GardeArgument { /// #[derive(Debug)] pub enum GardeRejection { - /// `Valid` variant captures errors related to the validation logic. It contains `ValidationErrors` + /// `Valid` variant captures errors related to the validation logic. It contains `garde::Report` /// which is a collection of validation failures for each field. Report(garde::Report), /// `Inner` variant represents potential errors that might occur within the inner extractor. @@ -134,43 +105,43 @@ impl IntoResponse for GardeRejection { } #[async_trait] -impl FromRequest for Garde +impl FromRequest for Garde where State: Send + Sync, Body: Send + Sync + 'static, - Args: Send + Sync + FromRef + GardeArgument::Validate>, + Context: Send + Sync + FromRef, Extractor: HasValidate + FromRequest, - ::Validate: garde::Validate, + ::Validate: garde::Validate, { type Rejection = GardeRejection<>::Rejection>; async fn from_request(req: Request, state: &State) -> Result { - let arguments: Args = FromRef::from_ref(state); + let context: Context = FromRef::from_ref(state); let inner = Extractor::from_request(req, state) .await .map_err(GardeRejection::Inner)?; - inner.get_validate().validate(&arguments.get())?; - Ok(Garde(inner, arguments)) + inner.get_validate().validate(&context)?; + Ok(Garde(inner)) } } #[async_trait] -impl FromRequestParts for Garde +impl FromRequestParts for Garde where State: Send + Sync, - Args: Send + Sync + FromRef + GardeArgument::Validate>, + Context: Send + Sync + FromRef, Extractor: HasValidate + FromRequestParts, - ::Validate: garde::Validate, + ::Validate: garde::Validate, { type Rejection = GardeRejection<>::Rejection>; async fn from_request_parts(parts: &mut Parts, state: &State) -> Result { - let arguments: Args = FromRef::from_ref(state); + let context: Context = FromRef::from_ref(state); let inner = Extractor::from_request_parts(parts, state) .await .map_err(GardeRejection::Inner)?; - inner.get_validate().validate(&arguments.get())?; - Ok(Garde(inner, arguments)) + inner.get_validate().validate(&context)?; + Ok(Garde(inner)) } } diff --git a/src/json.rs b/src/json.rs index 0068805..0640cf9 100644 --- a/src/json.rs +++ b/src/json.rs @@ -40,9 +40,9 @@ use crate::{HasValidate, HasValidateArgs}; use axum::Json; -use validator::{Validate, ValidateArgs}; +use validator::ValidateArgs; -impl HasValidate for Json { +impl HasValidate for Json { type Validate = T; fn get_validate(&self) -> &T { &self.0 diff --git a/src/lib.rs b/src/lib.rs index 8ca8cfc..b7174f0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -215,7 +215,7 @@ impl IntoResponse for ValidRejection { /// Trait for types that can supply a reference that can be validated. /// -/// Extractor types `T` that implement this trait can be used with `Valid`. +/// Extractor types `T` that implement this trait can be used with `Valid` or `Garde`. /// pub trait HasValidate { /// Inner type that can be validated for correctness diff --git a/src/msgpack.rs b/src/msgpack.rs index ec95332..fa94252 100644 --- a/src/msgpack.rs +++ b/src/msgpack.rs @@ -49,9 +49,9 @@ use crate::{HasValidate, HasValidateArgs}; use axum_msgpack::{MsgPack, MsgPackRaw}; -use validator::{Validate, ValidateArgs}; +use validator::ValidateArgs; -impl HasValidate for MsgPack { +impl HasValidate for MsgPack { type Validate = T; fn get_validate(&self) -> &T { &self.0 @@ -65,7 +65,7 @@ impl<'v, T: ValidateArgs<'v>> HasValidateArgs<'v> for MsgPack { } } -impl HasValidate for MsgPackRaw { +impl HasValidate for MsgPackRaw { type Validate = T; fn get_validate(&self) -> &T { &self.0 diff --git a/src/path.rs b/src/path.rs index e01f1c6..931731c 100644 --- a/src/path.rs +++ b/src/path.rs @@ -39,9 +39,9 @@ use crate::{HasValidate, HasValidateArgs}; use axum::extract::Path; -use validator::{Validate, ValidateArgs}; +use validator::ValidateArgs; -impl HasValidate for Path { +impl HasValidate for Path { type Validate = T; fn get_validate(&self) -> &T { &self.0 diff --git a/src/query.rs b/src/query.rs index 1292d0b..3e386b6 100644 --- a/src/query.rs +++ b/src/query.rs @@ -43,9 +43,9 @@ use crate::{HasValidate, HasValidateArgs}; use axum::extract::Query; -use validator::{Validate, ValidateArgs}; +use validator::ValidateArgs; -impl HasValidate for Query { +impl HasValidate for Query { type Validate = T; fn get_validate(&self) -> &T { &self.0 diff --git a/src/test.rs b/src/test.rs index ada4d58..e1b9856 100644 --- a/src/test.rs +++ b/src/test.rs @@ -1,3 +1,4 @@ +use crate::garde::Garde; use crate::tests::{ValidTest, ValidTestParameter}; use crate::{Arguments, HasValidate, HasValidateArgs, Valid, ValidEx, VALIDATION_ERROR_STATUS}; use axum::extract::{FromRef, Path, Query}; @@ -82,6 +83,21 @@ impl Default for ParametersExValidationArgumentsInner { } } +#[derive(Clone, Deserialize, Serialize, garde::Validate, Eq, PartialEq)] +#[cfg_attr(feature = "extra_protobuf", derive(prost::Message))] +#[cfg_attr( + feature = "typed_multipart", + derive(axum_typed_multipart::TryFromMultipart) +)] +pub struct ParametersGarde { + #[garde(range(min = 5, max = 10))] + #[cfg_attr(feature = "extra_protobuf", prost(int32, tag = "1"))] + v0: i32, + #[garde(length(min = 1, max = 10))] + #[cfg_attr(feature = "extra_protobuf", prost(string, tag = "2"))] + v1: String, +} + static VALID_PARAMETERS: Lazy = Lazy::new(|| Parameters { v0: 5, v1: String::from("0123456789"), @@ -129,6 +145,10 @@ struct MyState { typed_path_validation_ctx: extra_typed_path::TypedPathParamExValidationArguments, } +impl FromRef for () { + fn from_ref(_: &MyState) -> Self {} +} + #[tokio::test] async fn test_main() -> anyhow::Result<()> { let state = MyState { @@ -144,8 +164,10 @@ async fn test_main() -> anyhow::Result<()> { .route(route::JSON, post(extract_json)) .route(route::PATH_EX, get(extract_path_ex)) .route(route::QUERY_EX, get(extract_query_ex)) + .route(route::QUERY_GARDE, get(extract_query_garde)) .route(route::FORM_EX, post(extract_form_ex)) - .route(route::JSON_EX, post(extract_json_ex)); + .route(route::JSON_EX, post(extract_json_ex)) + .route(route::JSON_GARDE, post(extract_json_garde)); #[cfg(feature = "typed_header")] let router = router @@ -344,6 +366,11 @@ async fn test_main() -> anyhow::Result<()> { .execute::>(Method::GET, route::QUERY_EX) .await?; + // Garde + test_executor + .execute::>(Method::GET, route::QUERY_GARDE) + .await?; + // Valid test_executor .execute::>(Method::POST, route::FORM) @@ -364,6 +391,11 @@ async fn test_main() -> anyhow::Result<()> { .execute::>(Method::POST, route::JSON_EX) .await?; + // Garde + test_executor + .execute::>(Method::POST, route::JSON_GARDE) + .await?; + #[cfg(feature = "typed_header")] { use axum::TypedHeader; @@ -666,10 +698,12 @@ mod route { pub const PATH_EX: &str = "/path_ex/:v0/:v1"; pub const QUERY: &str = "/query"; pub const QUERY_EX: &str = "/query_ex"; + pub const QUERY_GARDE: &str = "/query_garde"; pub const FORM: &str = "/form"; pub const FORM_EX: &str = "/form_ex"; pub const JSON: &str = "/json"; pub const JSON_EX: &str = "/json_ex"; + pub const JSON_GARDE: &str = "/json_garde"; } async fn extract_path(Valid(Path(parameters)): Valid>) -> StatusCode { @@ -692,6 +726,12 @@ async fn extract_query_ex( validate_again_ex(parameters, args.get()) } +async fn extract_query_garde( + Garde(Query(parameters)): Garde>, +) -> StatusCode { + validate_again_garde(parameters, ()) +} + async fn extract_form(Valid(Form(parameters)): Valid>) -> StatusCode { validate_again(parameters) } @@ -712,6 +752,10 @@ async fn extract_json_ex( validate_again_ex(parameters, args.get()) } +async fn extract_json_garde(Garde(Json(parameters)): Garde>) -> StatusCode { + validate_again_garde(parameters, ()) +} + fn validate_again(validate: V) -> StatusCode { // The `Valid` extractor has validated the `parameters` once, // it should have returned `400 BAD REQUEST` if the `parameters` were invalid, @@ -727,9 +771,9 @@ fn validate_again_ex<'v, V: ValidateArgs<'v>>( validate: V, args: >::Args, ) -> StatusCode { - // The `Valid` extractor has validated the `parameters` once, + // The `ValidEx` extractor has validated the `parameters` once, // it should have returned `400 BAD REQUEST` if the `parameters` were invalid, - // Let's validate them again to check if the `Valid` extractor works well. + // Let's validate them again to check if the `ValidEx` extractor works well. // If it works properly, this function will never return `500 INTERNAL SERVER ERROR` match validate.validate_args(args) { Ok(_) => StatusCode::OK, @@ -737,6 +781,20 @@ fn validate_again_ex<'v, V: ValidateArgs<'v>>( } } +fn validate_again_garde(validate: V, context: V::Context) -> StatusCode +where + V: garde::Validate, +{ + // The `Garde` extractor has validated the `parameters` once, + // it should have returned `400 BAD REQUEST` if the `parameters` were invalid, + // Let's validate them again to check if the `Garde` extractor works well. + // If it works properly, this function will never return `500 INTERNAL SERVER ERROR` + match validate.validate(&context) { + Ok(_) => StatusCode::OK, + Err(_) => StatusCode::INTERNAL_SERVER_ERROR, + } +} + #[cfg(feature = "typed_header")] mod typed_header { diff --git a/src/typed_header.rs b/src/typed_header.rs index 8fae4be..c3b8f63 100644 --- a/src/typed_header.rs +++ b/src/typed_header.rs @@ -63,9 +63,9 @@ use crate::{HasValidate, HasValidateArgs}; use axum::TypedHeader; -use validator::{Validate, ValidateArgs}; +use validator::ValidateArgs; -impl HasValidate for TypedHeader { +impl HasValidate for TypedHeader { type Validate = T; fn get_validate(&self) -> &T { &self.0 diff --git a/src/typed_multipart.rs b/src/typed_multipart.rs index b989a2a..47f4663 100644 --- a/src/typed_multipart.rs +++ b/src/typed_multipart.rs @@ -52,9 +52,9 @@ use crate::{HasValidate, HasValidateArgs}; use axum_typed_multipart::{BaseMultipart, TypedMultipart}; -use validator::{Validate, ValidateArgs}; +use validator::ValidateArgs; -impl HasValidate for BaseMultipart { +impl HasValidate for BaseMultipart { type Validate = T; fn get_validate(&self) -> &T { &self.data @@ -68,7 +68,7 @@ impl<'v, T: ValidateArgs<'v>, R> HasValidateArgs<'v> for BaseMultipart { } } -impl HasValidate for TypedMultipart { +impl HasValidate for TypedMultipart { type Validate = T; fn get_validate(&self) -> &T { &self.0 diff --git a/src/yaml.rs b/src/yaml.rs index 0ebffc9..817be96 100644 --- a/src/yaml.rs +++ b/src/yaml.rs @@ -44,9 +44,9 @@ use crate::{HasValidate, HasValidateArgs}; use axum_yaml::Yaml; -use validator::{Validate, ValidateArgs}; +use validator::ValidateArgs; -impl HasValidate for Yaml { +impl HasValidate for Yaml { type Validate = T; fn get_validate(&self) -> &T { &self.0