Skip to content

Commit

Permalink
feat: add transaction validation
Browse files Browse the repository at this point in the history
  • Loading branch information
flpStrri committed Feb 7, 2024
1 parent 9547c9e commit 8503a2b
Show file tree
Hide file tree
Showing 10 changed files with 219 additions and 9 deletions.
85 changes: 84 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 5 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,16 @@ tracing-bunyan-formatter = "0.3"
tracing-subscriber = { version = "0.3", features = ["registry", "env-filter", "fmt", "json"] }
serde = { version = "1.0", features = ["derive"] }
chrono = { version = "0.4", features = ["serde"] }
mongodb = { version = "2.7", features = ["bson-uuid-1"] }
mongodb = { version = "2.8", features = ["bson-uuid-1"] }
uuid = { version = "1.7", features = ["serde", "v4"] }
futures = "0.3.29"
futures = "0.3.30"
hyper = "1.1.0"
config = { version = "0.13.4", features = [] }
tower-http = { version = "0.5.1", features = ["sensitive-headers", "trace", "util", "request-id"] }
tower = "0.4.13"
validator = { version = "0.16.1", features = ["derive"] }

[dev-dependencies]
reqwest = { version = "0.11.22", features = ["json"] }
reqwest = { version = "0.11.23", features = ["json"] }
serde_json = "1.0.111"
ulid = "1.0.0"
ulid = "1.1.0"
1 change: 1 addition & 0 deletions src/routes.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod count_devs;
pub mod devs;
pub mod health_check;
pub mod transaction;
20 changes: 20 additions & 0 deletions src/routes/transaction.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use axum::extract::{Path, State};
use axum::{http::StatusCode, response::IntoResponse, Json};
use mongodb::Database;

use crate::structs::api;

#[tracing::instrument(name = "Creating a new transaction", skip(_client, _body))]
pub async fn create_transaction(
State(_client): State<Database>,
Path(id): Path<u64>,
Json(_body): Json<api::Transaction>,
) -> impl IntoResponse {
(
StatusCode::OK,
Json(api::Balance {
balance: 123,
limit: 456,
}),
)
}
4 changes: 4 additions & 0 deletions src/startup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ impl Application {
.route("/pessoas", post(routes::devs::create_person))
.route("/pessoas", get(routes::devs::search_persons))
.route("/contagem-pessoas", get(routes::count_devs::count_persons))
.route(
"/clientes/:id/transacoes",
post(routes::transaction::create_transaction),
)
.layer(tracing_middleware)
.route("/health-check", get(routes::health_check::health_check))
.with_state(mongodb_pool);
Expand Down
1 change: 1 addition & 0 deletions src/structs.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub mod api;
pub mod person;
pub mod transaction;
23 changes: 23 additions & 0 deletions src/structs/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ use chrono::NaiveDate;
use serde::{Deserialize, Serialize};
use uuid::Uuid;

use validator::Validate;

use crate::structs::transaction;

#[derive(Debug, Default, Deserialize, Serialize)]
pub struct CreatePersonBody {
#[serde(rename(deserialize = "apelido", serialize = "apelido"))]
Expand Down Expand Up @@ -33,3 +37,22 @@ pub struct PersonBody {
#[serde(rename(serialize = "stack", deserialize = "stack"))]
pub stacks: Option<Vec<String>>,
}

#[derive(Debug, Serialize, Deserialize, Validate, PartialEq)]
pub struct Transaction {
#[serde(rename(serialize = "valor", deserialize = "valor"))]
pub value: u64,
#[serde(rename(serialize = "descricao", deserialize = "descricao"))]
#[validate(length(min = 1, max = 10))]
pub description: String,
#[serde(rename(serialize = "tipo", deserialize = "tipo"))]
pub action: transaction::Action,
}

#[derive(Debug, Default, Serialize, Deserialize, PartialEq)]
pub struct Balance {
#[serde(rename(serialize = "limite", deserialize = "limite"))]
pub limit: u64,
#[serde(rename(serialize = "saldo", deserialize = "saldo"))]
pub balance: i64,
}
16 changes: 16 additions & 0 deletions src/structs/transaction.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub enum Action {
#[serde(rename = "c")]
Credit,
#[serde(rename = "d")]
Debt,
}

#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct Transaction {
pub action: Action,
pub description: String,
pub value: i64,
}
7 changes: 3 additions & 4 deletions tests/api/main.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
mod count_devs;
mod get_dev_by_id;
mod get_devs_by_search_term;
mod health_check;
pub mod helpers;
mod post_devs;

mod count_devs;

mod get_devs_by_search_term;
mod post_transaction;
62 changes: 62 additions & 0 deletions tests/api/post_transaction.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use reqwest::StatusCode;

#[tokio::test]
async fn returns_200_with_balance_body_given_a_valid_request() {
let test_app = crate::helpers::spawn_app().await;

let response = reqwest::Client::new()
.post(format!("{}/clientes/1/transacoes", test_app.address))
.json(&serde_json::json!({
"valor": 1000,
"tipo" : "d",
"descricao" : "descricao"
}))
.send()
.await
.expect("failed request");

assert_eq!(response.status(), StatusCode::OK);
let response_body = response
.json::<std::collections::HashMap<String, serde_json::Value>>()
.await
.unwrap();
assert_eq!(response_body["saldo"], 123);
assert_eq!(response_body["limite"], 456);
}

#[tokio::test]
async fn returns_422_with_error_body_given_a_too_long_description() {
let test_app = crate::helpers::spawn_app().await;

let response = reqwest::Client::new()
.post(format!("{}/clientes/1/transacoes", test_app.address))
.json(&serde_json::json!({
"valor": 1000,
"tipo" : "d",
"descricao" : "some important money that I must send you!"
}))
.send()
.await
.expect("failed request");

assert_eq!(response.status(), StatusCode::UNPROCESSABLE_ENTITY);
}

#[tokio::test]
async fn returns_422_with_error_body_given_a_too_short_description() {
let test_app = crate::helpers::spawn_app().await;

let response = reqwest::Client::new()
.post(format!("{}/clientes/1/transacoes", test_app.address))
.json(&serde_json::json!({
"valor": 1000,
"tipo" : "d",
"descricao" : ""
}))
.send()
.await
.expect("failed request");

assert_eq!(response.status(), StatusCode::UNPROCESSABLE_ENTITY);
}

0 comments on commit 8503a2b

Please sign in to comment.