diff --git a/Cargo.lock b/Cargo.lock index b73a8c3b..ad8a8e8d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1951,9 +1951,11 @@ name = "example-validator" version = "0.1.0" dependencies = [ "axum", + "http-body-util", "serde", "thiserror 1.0.69", "tokio", + "tower 0.5.2", "tracing", "tracing-subscriber", "validator", diff --git a/examples/validator/Cargo.toml b/examples/validator/Cargo.toml index 8a7e6928..601e0e2c 100644 --- a/examples/validator/Cargo.toml +++ b/examples/validator/Cargo.toml @@ -12,3 +12,7 @@ tokio = { version = "1.0", features = ["full"] } tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } validator = { version = "0.18.1", features = ["derive"] } + +[dev-dependencies] +http-body-util = "0.1.0" +tower = { version = "0.5.2", features = ["util"] } diff --git a/examples/validator/src/main.rs b/examples/validator/src/main.rs index 00e46173..f02e2ca9 100644 --- a/examples/validator/src/main.rs +++ b/examples/validator/src/main.rs @@ -34,7 +34,7 @@ async fn main() { .init(); // build our application with a route - let app = Router::new().route("/", get(handler)); + let app = app(); // run it let listener = TcpListener::bind("127.0.0.1:3000").await.unwrap(); @@ -42,9 +42,13 @@ async fn main() { axum::serve(listener, app).await.unwrap(); } +fn app() -> Router { + Router::new().route("/", get(handler)) +} + #[derive(Debug, Deserialize, Validate)] pub struct NameInput { - #[validate(length(min = 1, message = "Can not be empty"))] + #[validate(length(min = 2, message = "Can not be empty"))] pub name: String, } @@ -91,3 +95,83 @@ impl IntoResponse for ServerError { .into_response() } } + +#[cfg(test)] +mod tests { + use super::*; + use axum::{ + body::Body, + http::{Request, StatusCode}, + }; + use http_body_util::BodyExt; + use tower::ServiceExt; + + async fn get_html(response: Response) -> String { + let body = response.into_body(); + let bytes = body.collect().await.unwrap().to_bytes(); + String::from_utf8(bytes.to_vec()).unwrap() + } + + #[tokio::test] + async fn test_no_param() { + let response = app() + .oneshot(Request::builder().uri("/").body(Body::empty()).unwrap()) + .await + .unwrap(); + + assert_eq!(response.status(), StatusCode::BAD_REQUEST); + let html = get_html(response).await; + assert_eq!(html, "Failed to deserialize form: missing field `name`"); + } + + #[tokio::test] + async fn test_with_param_without_value() { + let response = app() + .oneshot( + Request::builder() + .uri("/?name=") + .body(Body::empty()) + .unwrap(), + ) + .await + .unwrap(); + + assert_eq!(response.status(), StatusCode::BAD_REQUEST); + let html = get_html(response).await; + assert_eq!(html, "Input validation error: [name: Can not be empty]"); + } + + #[tokio::test] + async fn test_with_param_with_short_value() { + let response = app() + .oneshot( + Request::builder() + .uri("/?name=X") + .body(Body::empty()) + .unwrap(), + ) + .await + .unwrap(); + + assert_eq!(response.status(), StatusCode::BAD_REQUEST); + let html = get_html(response).await; + assert_eq!(html, "Input validation error: [name: Can not be empty]"); + } + + #[tokio::test] + async fn test_with_param_and_value() { + let response = app() + .oneshot( + Request::builder() + .uri("/?name=LT") + .body(Body::empty()) + .unwrap(), + ) + .await + .unwrap(); + + assert_eq!(response.status(), StatusCode::OK); + let html = get_html(response).await; + assert_eq!(html, "

Hello, LT!

"); + } +}