#![cfg(feature = "validator")]
use axum::{Router, routing::{post, get}, http::{Request, StatusCode}};
use axum::body::to_bytes;
use axum::body::Body;
use tower::ServiceExt; use serde::{Deserialize, Serialize};
use validator::Validate;
#[derive(Debug, Deserialize, Serialize, Validate)]
struct User {
#[validate(length(min = 1))]
name: String,
#[validate(range(min = 18))]
age: u8,
}
async fn create_user(spring_axum::ValidatedJson(user): spring_axum::ValidatedJson<User>) -> String {
format!("ok:{}:{}", user.name, user.age)
}
#[derive(Debug, Deserialize, Validate)]
struct SearchParams {
#[validate(length(min = 2))]
q: String,
}
async fn search(spring_axum::ValidatedQuery(params): spring_axum::ValidatedQuery<SearchParams>) -> String {
format!("found:{}", params.q)
}
#[tokio::test]
async fn json_validation_failure_and_success() {
let app = Router::new()
.route("/users", post(create_user));
let req = Request::builder()
.method("POST")
.uri("/users")
.header("content-type", "application/json")
.body(Body::from(r#"{"name":"","age":17}"#))
.unwrap();
let res = app.clone().oneshot(req).await.unwrap();
assert_eq!(res.status(), StatusCode::BAD_REQUEST);
let body = to_bytes(res.into_body(), usize::MAX).await.unwrap();
let v: serde_json::Value = serde_json::from_slice(&body).unwrap();
assert!(v.get("errors").is_some(), "expected structured validation errors");
let errs = v.get("errors").unwrap();
assert!(errs.get("name").is_some());
assert!(errs.get("age").is_some());
let req_ok = Request::builder()
.method("POST")
.uri("/users")
.header("content-type", "application/json")
.body(Body::from(r#"{"name":"alice","age":18}"#))
.unwrap();
let res_ok = app.clone().oneshot(req_ok).await.unwrap();
assert_eq!(res_ok.status(), StatusCode::OK);
let body_ok = to_bytes(res_ok.into_body(), usize::MAX).await.unwrap();
assert_eq!(std::str::from_utf8(&body_ok).unwrap(), "ok:alice:18");
}
#[tokio::test]
async fn query_validation_failure_and_success() {
let app = Router::new()
.route("/search", get(search));
let req = Request::builder()
.method("GET")
.uri("/search?q=a")
.body(Body::empty())
.unwrap();
let res = app.clone().oneshot(req).await.unwrap();
assert_eq!(res.status(), StatusCode::BAD_REQUEST);
let body = to_bytes(res.into_body(), usize::MAX).await.unwrap();
let v: serde_json::Value = serde_json::from_slice(&body).unwrap();
assert!(v.get("errors").is_some());
assert!(v.get("errors").unwrap().get("q").is_some());
let req_ok = Request::builder()
.method("GET")
.uri("/search?q=rust")
.body(Body::empty())
.unwrap();
let res_ok = app.clone().oneshot(req_ok).await.unwrap();
assert_eq!(res_ok.status(), StatusCode::OK);
let body_ok = to_bytes(res_ok.into_body(), usize::MAX).await.unwrap();
assert_eq!(std::str::from_utf8(&body_ok).unwrap(), "found:rust");
}