diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml new file mode 100644 index 0000000..b250fb0 --- /dev/null +++ b/.github/workflows/check.yaml @@ -0,0 +1,56 @@ +name: Check + +on: + workflow_dispatch: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +env: + CARGO_TERM_COLOR: always + +# TODO: Maybe use a matrix and test multiple rust versions +jobs: + lint: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - uses: dtolnay/rust-toolchain@1.79.0 + with: + components: clippy, rustfmt + + - uses: Swatinem/rust-cache@v2 + + - run: cargo fmt --check + - run: cargo clippy -- -D warnings + + + test: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - uses: dtolnay/rust-toolchain@1.79.0 + with: + components: llvm-tools-preview + + - uses: Swatinem/rust-cache@v2 + + - run: cargo install cargo-llvm-cov + + - name: cargo llvm-cov + run: cargo llvm-cov --lcov --output-path lcov.info --all-features + + - name: Record Rust version + run: echo "RUST=$(rustc --version)" >> "$GITHUB_ENV" + + - name: Upload to codecov.io + uses: codecov/codecov-action@v4 + with: + fail_ci_if_error: true + token: ${{ secrets.CODECOV_TOKEN }} + env_vars: OS,RUST diff --git a/Cargo.toml b/Cargo.toml index 0a35e30..d8565c9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,11 @@ thiserror = "1" validator = { version = "0.18", optional = true } garde = { version = "0.20", optional = true } +[dev-dependencies] +serde = { version = "1", features = ["derive"]} +validator = { version = "0.18", features = ["derive"] } +garde = { version = "0.20", features = ["derive"] } + [features] # Use validator crate (https://github.com/Keats/validator) as the validation implmentation diff --git a/README.md b/README.md index 8af5b58..399f8bf 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ actix-web-validation = { version = "0.0.0", features = ["validator"]} actix-web-validation = { version = "0.0.0", features = ["garde"]} ``` -```rust +```rust,ignore use actix_web_validation::validator::Validated; use validator::Validate; @@ -53,7 +53,7 @@ async fn hello(Validated(Json(payload)): Validated>) -> impl Respo Custom error responses can achieved by providing an error handler. Below is an example custom error response that responds with JSON -```rust +```rust,ignore #[derive(Debug, Serialize, Error)] struct CustomErrorResponse { custom_message: String, @@ -75,7 +75,7 @@ impl ResponseError for CustomErrorResponse { Below is an example for the `validator` crate -```rust +```rust,ignore fn error_handler(errors: ::validator::ValidationErrors, req: &HttpRequest) -> actix_web::Error { CustomErrorResponse { custom_message: "My custom message".to_string(), @@ -103,7 +103,7 @@ async fn main() -> std::io::Result<()> { Below is an example for the `garde` crate -```rust +```rust,ignore fn error_handler(errors: ::garde::Report, req: &HttpRequest) -> actix_web::Error { CustomErrorResponse { custom_message: "My custom message".to_string(), diff --git a/src/garde.rs b/src/garde.rs index e26b4b6..5b755e7 100644 --- a/src/garde.rs +++ b/src/garde.rs @@ -21,8 +21,9 @@ use thiserror::Error; /// use actix_web::{post, web::{self, Json}, App}; /// use serde::Deserialize; /// use garde::Validate; +/// use actix_web_validation::garde::Validated; /// -/// #[derive(Deserialize, Validate)] +/// #[derive(Debug, Deserialize, Validate)] /// struct Info { /// #[garde(length(min = 3))] /// username: String, @@ -176,3 +177,50 @@ where self.app_data(GardeErrorHandler { handler }) } } + +#[cfg(test)] +mod test { + use super::*; + use actix_web::{http::header::ContentType, post, test, web::Json, App, Responder}; + use garde::Validate; + use serde::{Deserialize, Serialize}; + + #[actix_web::test] + async fn should_validate_simple() { + #[derive(Debug, Deserialize, Serialize, Validate)] + struct ExamplePayload { + #[garde(length(min = 5))] + name: String, + } + + #[post("/")] + async fn endpoint(v: Validated>) -> impl Responder { + assert!(v.name.len() > 4); + HttpResponse::Ok().body(()) + } + + let app = test::init_service(App::new().service(endpoint)).await; + + // Valid request + let req = test::TestRequest::post() + .uri("/") + .insert_header(ContentType::plaintext()) + .set_json(ExamplePayload { + name: "123456".to_string(), + }) + .to_request(); + let resp = test::call_service(&app, req).await; + assert_eq!(resp.status().as_u16(), 200); + + // Invalid request + let req = test::TestRequest::post() + .uri("/") + .insert_header(ContentType::plaintext()) + .set_json(ExamplePayload { + name: "1234".to_string(), + }) + .to_request(); + let resp = test::call_service(&app, req).await; + assert_eq!(resp.status().as_u16(), 400); + } +} diff --git a/src/validator.rs b/src/validator.rs index 93d72bc..855f3ff 100644 --- a/src/validator.rs +++ b/src/validator.rs @@ -22,8 +22,9 @@ use validator::{ValidationError, ValidationErrors, ValidationErrorsKind}; /// use actix_web::{post, web::{self, Json}, App}; /// use serde::Deserialize; /// use validator::Validate; +/// use actix_web_validation::validator::Validated; /// -/// #[derive(Deserialize, Validate)] +/// #[derive(Debug, Deserialize, Validate)] /// struct Info { /// #[validate(length(min = 5))] /// username: String, @@ -216,3 +217,50 @@ where self.app_data(ValidatorErrorHandler { handler }) } } + +#[cfg(test)] +mod test { + use super::*; + use actix_web::{http::header::ContentType, post, test, web::Json, App, Responder}; + use serde::{Deserialize, Serialize}; + use validator::Validate; + + #[actix_web::test] + async fn should_validate_simple() { + #[derive(Debug, Deserialize, Serialize, Validate)] + struct ExamplePayload { + #[validate(length(min = 5))] + name: String, + } + + #[post("/")] + async fn endpoint(v: Validated>) -> impl Responder { + assert!(v.name.len() > 4); + HttpResponse::Ok().body(()) + } + + let app = test::init_service(App::new().service(endpoint)).await; + + // Valid request + let req = test::TestRequest::post() + .uri("/") + .insert_header(ContentType::plaintext()) + .set_json(ExamplePayload { + name: "123456".to_string(), + }) + .to_request(); + let resp = test::call_service(&app, req).await; + assert_eq!(resp.status().as_u16(), 200); + + // Invalid request + let req = test::TestRequest::post() + .uri("/") + .insert_header(ContentType::plaintext()) + .set_json(ExamplePayload { + name: "1234".to_string(), + }) + .to_request(); + let resp = test::call_service(&app, req).await; + assert_eq!(resp.status().as_u16(), 400); + } +}