add tests

This commit is contained in:
gengteng
2023-09-28 23:59:42 +08:00
parent 9b28922201
commit e4f54a5cb9
2 changed files with 339 additions and 54 deletions

View File

@@ -159,14 +159,13 @@ impl<Arguments> ValidationContext<Arguments> {
/// # Arguments
///
/// `T`: data type to validate
/// `Self::A`: dependent arguments
/// * `T`: The data type to validate using arguments
///
pub trait Arguments<'a, T>
where
T: ValidateArgs<'a>,
{
/// Get dependent arguments
/// Get arguments from `self`
fn get(&'a self) -> T::Args;
}

View File

@@ -1,3 +1,4 @@
use crate::test::extra_typed_path::TypedPathParamExValidationArguments;
use crate::tests::{ValidTest, ValidTestParameter};
use crate::{Arguments, HasValidate, Valid, ValidEx, ValidationContext, VALIDATION_ERROR_STATUS};
use axum::extract::{FromRef, Path, Query};
@@ -109,31 +110,36 @@ impl HasValidate for Parameters {
#[derive(Debug, Clone, FromRef)]
struct MyState {
validation_ctx: ValidationContext<ParametersExValidationArguments>,
param_validation_ctx: ValidationContext<ParametersExValidationArguments>,
typed_path_validation_ctx: ValidationContext<TypedPathParamExValidationArguments>,
}
#[tokio::test]
async fn test_main() -> anyhow::Result<()> {
let state = MyState {
param_validation_ctx: ValidationContext::<ParametersExValidationArguments>::default(),
typed_path_validation_ctx:
ValidationContext::<TypedPathParamExValidationArguments>::default(),
};
let router = Router::new()
.route(route::PATH, get(extract_path))
.route(route::QUERY, get(extract_query))
.route(route::FORM, post(extract_form))
.route(route::JSON, post(extract_json));
let router_ex = Router::new()
.route(route::JSON, post(extract_json))
.route(route::QUERY_EX, get(extract_query_ex))
.route(route::FORM_EX, post(extract_form_ex))
.route(route::JSON_EX, post(extract_json_ex))
.with_state(MyState {
validation_ctx: ValidationContext::<ParametersExValidationArguments>::default(),
});
let router = router.merge(router_ex);
.route(route::JSON_EX, post(extract_json_ex));
#[cfg(feature = "typed_header")]
let router = router.route(
let router = router
.route(
typed_header::route::TYPED_HEADER,
post(typed_header::extract_typed_header),
)
.route(
typed_header::route::TYPED_HEADER_EX,
post(typed_header::extract_typed_header_ex),
);
#[cfg(feature = "typed_multipart")]
@@ -142,9 +148,17 @@ async fn test_main() -> anyhow::Result<()> {
typed_multipart::route::TYPED_MULTIPART,
post(typed_multipart::extract_typed_multipart),
)
.route(
typed_multipart::route::TYPED_MULTIPART_EX,
post(typed_multipart::extract_typed_multipart_ex),
)
.route(
typed_multipart::route::BASE_MULTIPART,
post(typed_multipart::extract_base_multipart),
)
.route(
typed_multipart::route::BASE_MULTIPART_EX,
post(typed_multipart::extract_base_multipart_ex),
);
#[cfg(feature = "extra")]
@@ -160,40 +174,72 @@ async fn test_main() -> anyhow::Result<()> {
);
#[cfg(feature = "extra_typed_path")]
let router = router.route(
let router = router
.route(
extra_typed_path::route::EXTRA_TYPED_PATH,
get(extra_typed_path::extract_extra_typed_path),
)
.route(
extra_typed_path::route::EXTRA_TYPED_PATH_EX,
get(extra_typed_path::extract_extra_typed_path_ex),
);
#[cfg(feature = "extra_query")]
let router = router.route(
let router = router
.route(
extra_query::route::EXTRA_QUERY,
post(extra_query::extract_extra_query),
)
.route(
extra_query::route::EXTRA_QUERY_EX,
post(extra_query::extract_extra_query_ex),
);
#[cfg(feature = "extra_form")]
let router = router.route(
let router = router
.route(
extra_form::route::EXTRA_FORM,
post(extra_form::extract_extra_form),
)
.route(
extra_form::route::EXTRA_FORM_EX,
post(extra_form::extract_extra_form_ex),
);
#[cfg(feature = "extra_protobuf")]
let router = router.route(
let router = router
.route(
extra_protobuf::route::EXTRA_PROTOBUF,
post(extra_protobuf::extract_extra_protobuf),
)
.route(
extra_protobuf::route::EXTRA_PROTOBUF_EX,
post(extra_protobuf::extract_extra_protobuf_ex),
);
#[cfg(feature = "yaml")]
let router = router.route(yaml::route::YAML, post(yaml::extract_yaml));
let router = router
.route(yaml::route::YAML, post(yaml::extract_yaml))
.route(yaml::route::YAML_EX, post(yaml::extract_yaml_ex));
#[cfg(feature = "msgpack")]
let router = router
.route(msgpack::route::MSGPACK, post(msgpack::extract_msgpack))
.route(
msgpack::route::MSGPACK_EX,
post(msgpack::extract_msgpack_ex),
)
.route(
msgpack::route::MSGPACK_RAW,
post(msgpack::extract_msgpack_raw),
)
.route(
msgpack::route::MSGPACK_RAW_EX,
post(msgpack::extract_msgpack_raw_ex),
);
let router = router.with_state(state);
let server = axum::Server::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16)))
.serve(router.into_make_service());
let server_addr = server.local_addr();
@@ -288,26 +334,52 @@ async fn test_main() -> anyhow::Result<()> {
#[cfg(feature = "typed_header")]
{
use axum::TypedHeader;
// Valid
test_executor
.execute::<TypedHeader<Parameters>>(Method::POST, typed_header::route::TYPED_HEADER)
.await?;
// ValidEx
test_executor
.execute::<TypedHeader<Parameters>>(Method::POST, typed_header::route::TYPED_HEADER_EX)
.await?;
}
#[cfg(feature = "typed_multipart")]
{
use axum_typed_multipart::{BaseMultipart, TypedMultipart, TypedMultipartError};
// Valid
test_executor
.execute::<BaseMultipart<Parameters, TypedMultipartError>>(
Method::POST,
typed_multipart::route::BASE_MULTIPART,
)
.await?;
// ValidEx
test_executor
.execute::<BaseMultipart<Parameters, TypedMultipartError>>(
Method::POST,
typed_multipart::route::BASE_MULTIPART_EX,
)
.await?;
// Valid
test_executor
.execute::<TypedMultipart<Parameters>>(
Method::POST,
typed_multipart::route::TYPED_MULTIPART,
)
.await?;
// ValidEx
test_executor
.execute::<TypedMultipart<Parameters>>(
Method::POST,
typed_multipart::route::TYPED_MULTIPART_EX,
)
.await?;
}
#[cfg(feature = "extra")]
@@ -335,12 +407,16 @@ async fn test_main() -> anyhow::Result<()> {
#[cfg(feature = "extra_typed_path")]
{
{
async fn test_extra_typed_path(
test_executor: &TestExecutor,
route: &str,
server_url: &str,
) -> anyhow::Result<()> {
let extra_typed_path_type_name = "T: TypedPath";
let valid_extra_typed_path_response = test_executor
.client()
.get(format!(
"{}/extra_typed_path/{}/{}",
"{}/{route}/{}/{}",
server_url, VALID_PARAMETERS.v0, VALID_PARAMETERS.v1
))
.send()
@@ -354,7 +430,7 @@ async fn test_main() -> anyhow::Result<()> {
let error_extra_typed_path_response = test_executor
.client()
.get(format!("{}/extra_typed_path/not_i32/path", server_url))
.get(format!("{}/{route}/not_i32/path", server_url))
.send()
.await?;
assert_eq!(
@@ -367,7 +443,7 @@ async fn test_main() -> anyhow::Result<()> {
let invalid_extra_typed_path_response = test_executor
.client()
.get(format!(
"{}/extra_typed_path/{}/{}",
"{}/{route}/{}/{}",
server_url, INVALID_PARAMETERS.v0, INVALID_PARAMETERS.v1
))
.send()
@@ -385,7 +461,11 @@ async fn test_main() -> anyhow::Result<()> {
)
.await;
println!("All {} tests passed.", extra_typed_path_type_name);
Ok(())
}
test_extra_typed_path(&test_executor, "extra_typed_path", &server_url).await?;
test_extra_typed_path(&test_executor, "extra_typed_path_ex", &server_url).await?;
}
#[cfg(feature = "extra_query")]
@@ -394,6 +474,9 @@ async fn test_main() -> anyhow::Result<()> {
test_executor
.execute::<Query<Parameters>>(Method::POST, extra_query::route::EXTRA_QUERY)
.await?;
test_executor
.execute::<Query<Parameters>>(Method::POST, extra_query::route::EXTRA_QUERY_EX)
.await?;
}
#[cfg(feature = "extra_form")]
@@ -402,6 +485,9 @@ async fn test_main() -> anyhow::Result<()> {
test_executor
.execute::<Form<Parameters>>(Method::POST, extra_form::route::EXTRA_FORM)
.await?;
test_executor
.execute::<Form<Parameters>>(Method::POST, extra_form::route::EXTRA_FORM_EX)
.await?;
}
#[cfg(feature = "extra_protobuf")]
@@ -410,6 +496,9 @@ async fn test_main() -> anyhow::Result<()> {
test_executor
.execute::<Protobuf<Parameters>>(Method::POST, extra_protobuf::route::EXTRA_PROTOBUF)
.await?;
test_executor
.execute::<Protobuf<Parameters>>(Method::POST, extra_protobuf::route::EXTRA_PROTOBUF_EX)
.await?;
}
#[cfg(feature = "yaml")]
@@ -418,6 +507,9 @@ async fn test_main() -> anyhow::Result<()> {
test_executor
.execute::<Yaml<Parameters>>(Method::POST, yaml::route::YAML)
.await?;
test_executor
.execute::<Yaml<Parameters>>(Method::POST, yaml::route::YAML_EX)
.await?;
}
#[cfg(feature = "msgpack")]
@@ -426,9 +518,15 @@ async fn test_main() -> anyhow::Result<()> {
test_executor
.execute::<MsgPack<Parameters>>(Method::POST, msgpack::route::MSGPACK)
.await?;
test_executor
.execute::<MsgPack<Parameters>>(Method::POST, msgpack::route::MSGPACK_EX)
.await?;
test_executor
.execute::<MsgPackRaw<Parameters>>(Method::POST, msgpack::route::MSGPACK_RAW)
.await?;
test_executor
.execute::<MsgPackRaw<Parameters>>(Method::POST, msgpack::route::MSGPACK_RAW_EX)
.await?;
}
drop(server_guard);
@@ -589,10 +687,12 @@ mod typed_header {
pub(crate) mod route {
pub const TYPED_HEADER: &str = "/typed_header";
pub const TYPED_HEADER_EX: &str = "/typed_header_ex";
}
use super::{validate_again, Parameters};
use crate::Valid;
use crate::test::{validate_again_ex, ParametersEx, ParametersExValidationArguments};
use crate::{Arguments, Valid, ValidEx};
use axum::headers::{Error, Header, HeaderName, HeaderValue};
use axum::http::StatusCode;
use axum::TypedHeader;
@@ -605,6 +705,15 @@ mod typed_header {
validate_again(parameters)
}
pub(super) async fn extract_typed_header_ex(
ValidEx(TypedHeader(parameters), args): ValidEx<
TypedHeader<ParametersEx>,
ParametersExValidationArguments,
>,
) -> StatusCode {
validate_again_ex(parameters, args.get())
}
impl Header for Parameters {
fn name() -> &'static HeaderName {
&AXUM_VALID_PARAMETERS
@@ -638,6 +747,39 @@ mod typed_header {
}
}
impl Header for ParametersEx {
fn name() -> &'static HeaderName {
&AXUM_VALID_PARAMETERS
}
fn decode<'i, I>(values: &mut I) -> Result<Self, Error>
where
Self: Sized,
I: Iterator<Item = &'i HeaderValue>,
{
let value = values.next().ok_or_else(Error::invalid)?;
let src = std::str::from_utf8(value.as_bytes()).map_err(|_| Error::invalid())?;
let split = src.split(',').collect::<Vec<_>>();
match split.as_slice() {
[v0, v1] => Ok(ParametersEx {
v0: v0.parse().map_err(|_| Error::invalid())?,
v1: v1.to_string(),
}),
_ => Err(Error::invalid()),
}
}
fn encode<E: Extend<HeaderValue>>(&self, values: &mut E) {
let v0 = self.v0.to_string();
let mut vec = Vec::with_capacity(v0.len() + 1 + self.v1.len());
vec.extend_from_slice(v0.as_bytes());
vec.push(b',');
vec.extend_from_slice(self.v1.as_bytes());
let value = HeaderValue::from_bytes(&vec).expect("Failed to build header");
values.extend(::std::iter::once(value));
}
}
#[test]
fn parameter_is_header() -> anyhow::Result<()> {
let parameter = Parameters {
@@ -654,14 +796,19 @@ mod typed_header {
#[cfg(feature = "typed_multipart")]
mod typed_multipart {
use crate::test::{validate_again, Parameters};
use crate::Valid;
use crate::test::{
validate_again, validate_again_ex, Parameters, ParametersEx,
ParametersExValidationArguments,
};
use crate::{Arguments, Valid, ValidEx};
use axum::http::StatusCode;
use axum_typed_multipart::{BaseMultipart, TypedMultipart, TypedMultipartError};
pub mod route {
pub const TYPED_MULTIPART: &str = "/typed_multipart";
pub const TYPED_MULTIPART_EX: &str = "/typed_multipart_ex";
pub const BASE_MULTIPART: &str = "/base_multipart";
pub const BASE_MULTIPART_EX: &str = "/base_multipart_ex";
}
impl From<&Parameters> for reqwest::multipart::Form {
@@ -678,11 +825,29 @@ mod typed_multipart {
validate_again(parameters)
}
pub(super) async fn extract_typed_multipart_ex(
ValidEx(TypedMultipart(parameters), args): ValidEx<
TypedMultipart<ParametersEx>,
ParametersExValidationArguments,
>,
) -> StatusCode {
validate_again_ex(parameters, args.get())
}
pub(super) async fn extract_base_multipart(
Valid(BaseMultipart { data, .. }): Valid<BaseMultipart<Parameters, TypedMultipartError>>,
) -> StatusCode {
validate_again(data)
}
pub(super) async fn extract_base_multipart_ex(
ValidEx(BaseMultipart { data, .. }, args): ValidEx<
BaseMultipart<ParametersEx, TypedMultipartError>,
ParametersExValidationArguments,
>,
) -> StatusCode {
validate_again_ex(data, args.get())
}
}
#[cfg(feature = "extra")]
@@ -837,15 +1002,17 @@ mod extra {
#[cfg(feature = "extra_typed_path")]
mod extra_typed_path {
use crate::test::validate_again;
use crate::{HasValidate, Valid};
use crate::test::{validate_again, validate_again_ex};
use crate::{Arguments, HasValidate, HasValidateArgs, Valid, ValidEx};
use axum::http::StatusCode;
use axum_extra::routing::TypedPath;
use serde::Deserialize;
use validator::Validate;
use std::ops::RangeInclusive;
use validator::{Validate, ValidateArgs};
pub mod route {
pub const EXTRA_TYPED_PATH: &str = "/extra_typed_path/:v0/:v1";
pub const EXTRA_TYPED_PATH_EX: &str = "/extra_typed_path_ex/:v0/:v1";
}
#[derive(Validate, TypedPath, Deserialize)]
@@ -868,17 +1035,65 @@ mod extra_typed_path {
pub async fn extract_extra_typed_path(Valid(param): Valid<TypedPathParam>) -> StatusCode {
validate_again(param)
}
#[derive(Validate, TypedPath, Deserialize)]
#[typed_path("/extra_typed_path_ex/:v0/:v1")]
pub struct TypedPathParamEx {
#[validate(custom(function = "super::validate_v0", arg = "&'v_a RangeInclusive<i32>"))]
v0: i32,
#[validate(custom(function = "super::validate_v1", arg = "&'v_a RangeInclusive<usize>"))]
v1: String,
}
impl<'v> HasValidateArgs<'v> for TypedPathParamEx {
type ValidateArgs = Self;
fn get_validate_args(&self) -> &Self::ValidateArgs {
self
}
}
#[derive(Debug, Clone)]
pub struct TypedPathParamExValidationArguments {
v0_range: RangeInclusive<i32>,
v1_length_range: RangeInclusive<usize>,
}
impl Default for TypedPathParamExValidationArguments {
fn default() -> Self {
Self {
v0_range: 5..=10,
v1_length_range: 1..=10,
}
}
}
impl<'a> Arguments<'a, TypedPathParamEx> for TypedPathParamExValidationArguments {
fn get(&'a self) -> <TypedPathParamEx as ValidateArgs<'a>>::Args {
(&self.v0_range, &self.v1_length_range)
}
}
pub async fn extract_extra_typed_path_ex(
ValidEx(param, args): ValidEx<TypedPathParamEx, TypedPathParamExValidationArguments>,
) -> StatusCode {
validate_again_ex(param, args.get())
}
}
#[cfg(feature = "extra_query")]
mod extra_query {
use crate::test::{validate_again, Parameters};
use crate::Valid;
use crate::test::{
validate_again, validate_again_ex, Parameters, ParametersEx,
ParametersExValidationArguments,
};
use crate::{Arguments, Valid, ValidEx};
use axum::http::StatusCode;
use axum_extra::extract::Query;
pub mod route {
pub const EXTRA_QUERY: &str = "/extra_query";
pub const EXTRA_QUERY_EX: &str = "/extra_query_ex";
}
pub async fn extract_extra_query(
@@ -886,17 +1101,30 @@ mod extra_query {
) -> StatusCode {
validate_again(parameters)
}
pub async fn extract_extra_query_ex(
ValidEx(Query(parameters), args): ValidEx<
Query<ParametersEx>,
ParametersExValidationArguments,
>,
) -> StatusCode {
validate_again_ex(parameters, args.get())
}
}
#[cfg(feature = "extra_form")]
mod extra_form {
use crate::test::{validate_again, Parameters};
use crate::Valid;
use crate::test::{
validate_again, validate_again_ex, Parameters, ParametersEx,
ParametersExValidationArguments,
};
use crate::{Arguments, Valid, ValidEx};
use axum::http::StatusCode;
use axum_extra::extract::Form;
pub mod route {
pub const EXTRA_FORM: &str = "/extra_form";
pub const EXTRA_FORM_EX: &str = "/extra_form_ex";
}
pub async fn extract_extra_form(
@@ -904,17 +1132,30 @@ mod extra_form {
) -> StatusCode {
validate_again(parameters)
}
pub async fn extract_extra_form_ex(
ValidEx(Form(parameters), args): ValidEx<
Form<ParametersEx>,
ParametersExValidationArguments,
>,
) -> StatusCode {
validate_again_ex(parameters, args.get())
}
}
#[cfg(feature = "extra_protobuf")]
mod extra_protobuf {
use crate::test::{validate_again, Parameters};
use crate::Valid;
use crate::test::{
validate_again, validate_again_ex, Parameters, ParametersEx,
ParametersExValidationArguments,
};
use crate::{Arguments, Valid, ValidEx};
use axum::http::StatusCode;
use axum_extra::protobuf::Protobuf;
pub mod route {
pub const EXTRA_PROTOBUF: &str = "/extra_protobuf";
pub const EXTRA_PROTOBUF_EX: &str = "/extra_protobuf_ex";
}
pub async fn extract_extra_protobuf(
@@ -922,34 +1163,61 @@ mod extra_protobuf {
) -> StatusCode {
validate_again(parameters)
}
pub async fn extract_extra_protobuf_ex(
ValidEx(Protobuf(parameters), args): ValidEx<
Protobuf<ParametersEx>,
ParametersExValidationArguments,
>,
) -> StatusCode {
validate_again_ex(parameters, args.get())
}
}
#[cfg(feature = "yaml")]
mod yaml {
use crate::test::{validate_again, Parameters};
use crate::Valid;
use crate::test::{
validate_again, validate_again_ex, Parameters, ParametersEx,
ParametersExValidationArguments,
};
use crate::{Arguments, Valid, ValidEx};
use axum::http::StatusCode;
use axum_yaml::Yaml;
pub mod route {
pub const YAML: &str = "/yaml";
pub const YAML_EX: &str = "/yaml_ex";
}
pub async fn extract_yaml(Valid(Yaml(parameters)): Valid<Yaml<Parameters>>) -> StatusCode {
validate_again(parameters)
}
pub async fn extract_yaml_ex(
ValidEx(Yaml(parameters), args): ValidEx<
Yaml<ParametersEx>,
ParametersExValidationArguments,
>,
) -> StatusCode {
validate_again_ex(parameters, args.get())
}
}
#[cfg(feature = "msgpack")]
mod msgpack {
use crate::test::{validate_again, Parameters};
use crate::Valid;
use crate::test::{
validate_again, validate_again_ex, Parameters, ParametersEx,
ParametersExValidationArguments,
};
use crate::{Arguments, Valid, ValidEx};
use axum::http::StatusCode;
use axum_msgpack::{MsgPack, MsgPackRaw};
pub mod route {
pub const MSGPACK: &str = "/msgpack";
pub const MSGPACK_EX: &str = "/msgpack_ex";
pub const MSGPACK_RAW: &str = "/msgpack_raw";
pub const MSGPACK_RAW_EX: &str = "/msgpack_raw_ex";
}
pub async fn extract_msgpack(
@@ -958,9 +1226,27 @@ mod msgpack {
validate_again(parameters)
}
pub async fn extract_msgpack_ex(
ValidEx(MsgPack(parameters), args): ValidEx<
MsgPack<ParametersEx>,
ParametersExValidationArguments,
>,
) -> StatusCode {
validate_again_ex(parameters, args.get())
}
pub async fn extract_msgpack_raw(
Valid(MsgPackRaw(parameters)): Valid<MsgPackRaw<Parameters>>,
) -> StatusCode {
validate_again(parameters)
}
pub async fn extract_msgpack_raw_ex(
ValidEx(MsgPackRaw(parameters), args): ValidEx<
MsgPackRaw<ParametersEx>,
ParametersExValidationArguments,
>,
) -> StatusCode {
validate_again_ex(parameters, args.get())
}
}