#[macro_use] extern crate log; use std::sync::RwLock; use actix_cors::Cors; use actix_web::middleware::TrailingSlash; use actix_web::web::{Data, ServiceConfig}; use actix_web::{middleware, web, App, HttpServer}; use bank::bank::Bank; use crate::handlers::close_account; use crate::handlers::create_account; use crate::handlers::deposit; use crate::handlers::get_account; use crate::handlers::get_account_nrs; use crate::handlers::is_active; use crate::handlers::pong; use crate::handlers::transfer; use crate::handlers::update_account; use crate::handlers::withdraw; pub struct AppState { bank: RwLock, } mod handlers; fn config_app(app_data: Data) -> Box { Box::new(move |cfg: &mut ServiceConfig| { cfg.app_data(app_data.clone()).service(pong::route).service( web::scope("/accounts") .service(create_account::route) .service(get_account::route) .service(deposit::route) .service(close_account::route) .service(get_account_nrs::route) .service(withdraw::route) .service(transfer::route) .service(is_active::route) .service(update_account::route), ); }) } #[actix_web::main] async fn main() -> std::io::Result<()> { pretty_env_logger::init(); let app_data = web::Data::new(AppState { bank: RwLock::new(Bank::new()), }); HttpServer::new(move || { let cors = Cors::permissive(); App::new() .wrap(cors) .wrap(middleware::NormalizePath::new(TrailingSlash::Trim)) .configure(config_app(app_data.clone())) }) .bind(("127.0.0.1", 8000))? .run() .await } #[cfg(test)] mod tests { use actix_web::body::to_bytes; use actix_web::dev::Service; use actix_web::http::{Method as HttpMethod, StatusCode}; use actix_web::{test, App}; use serde_json::Value; use crate::create_account::OwnerData; use crate::handlers::AmountData; use crate::transfer::TransferData; use super::*; #[actix_web::test] async fn test_routes() { // TODO: split up into separate tests let app_data = web::Data::new(AppState { bank: RwLock::new(Bank::new()), }); let app = test::init_service( App::new() .wrap(middleware::NormalizePath::new(TrailingSlash::Trim)) .configure(config_app(app_data)), ) .await; let payload = OwnerData { owner: "aaa".to_string(), }; let req = test::TestRequest::post() .uri("/accounts") .set_form(&payload) .to_request(); let resp = test::call_service(&app, req).await; assert_eq!(resp.status(), StatusCode::CREATED); let body_bytes = to_bytes(resp.into_body()).await.unwrap(); let response_json: Value = serde_json::from_slice(&body_bytes).unwrap(); let nr = response_json["nr"].as_str().unwrap(); assert!(!nr.is_empty()); let req = test::TestRequest::default() .method(HttpMethod::HEAD) .uri(&format!("/accounts/{}", nr)) .to_request(); let resp = test::call_service(&app, req).await; assert!(resp.status().is_success()); let req = test::TestRequest::default() .method(HttpMethod::HEAD) .uri("/accounts/muh") .to_request(); let resp = test::call_service(&app, req).await; assert_eq!(resp.status(), StatusCode::NOT_FOUND); let payload = AmountData { amount: 5_f64 }; let req = test::TestRequest::patch() .uri(&format!("/accounts/{}/withdraw", nr)) .set_form(&payload) .to_request(); let resp = test::call_service(&app, req).await; assert!(!resp.status().is_success()); let req = test::TestRequest::patch() .uri(&format!("/accounts/{}/deposit", nr)) .set_form(&payload) .to_request(); let resp = test::call_service(&app, req).await; assert!(resp.status().is_success()); let req = test::TestRequest::get() .uri(&format!("/accounts/{}", nr)) .to_request(); let resp = test::call_service(&app, req).await; assert!(resp.status().is_success()); let body_bytes = to_bytes(resp.into_body()).await.unwrap(); let response_json: Value = serde_json::from_slice(&body_bytes).unwrap(); let balance = response_json["balance"].as_f64().unwrap(); assert_eq!(5_f64, balance); let payload = AmountData { amount: 10_f64 }; let req = test::TestRequest::put() .uri(&format!("/accounts/{}", nr)) .set_form(&payload) .to_request(); let resp = test::call_service(&app, req).await; assert!(resp.status().is_success()); let req = test::TestRequest::put() .uri("/accounts/muh") .set_form(&payload) .to_request(); let resp = test::call_service(&app, req).await; assert!(resp.status().is_client_error()); let payload = AmountData { amount: -10_f64 }; let req = test::TestRequest::put() .uri(&format!("/accounts/{}", nr)) .set_form(&payload) .to_request(); let resp = test::call_service(&app, req).await; assert!(resp.status().is_client_error()); let req = test::TestRequest::get() .uri(&format!("/accounts/{}", nr)) .to_request(); let resp = test::call_service(&app, req).await; assert!(resp.status().is_success()); let body_bytes = to_bytes(resp.into_body()).await.unwrap(); let response_json: Value = serde_json::from_slice(&body_bytes).unwrap(); let balance = response_json["balance"].as_f64().unwrap(); assert_eq!(10_f64, balance); let payload = OwnerData { owner: "bbb".to_string(), }; let req = test::TestRequest::post() .uri("/accounts") .set_form(&payload) .to_request(); let resp = test::call_service(&app, req).await; assert_eq!(resp.status(), StatusCode::CREATED); let body_bytes = to_bytes(resp.into_body()).await.unwrap(); let response_json: Value = serde_json::from_slice(&body_bytes).unwrap(); let nr2 = response_json["nr"].as_str().unwrap(); let payload = TransferData { from: nr.to_string(), to: nr2.to_string(), amount: 5_f64, }; let req = test::TestRequest::patch() .uri("/accounts/transfer") .set_form(&payload) .to_request(); let resp = test::call_service(&app, req).await; assert!(resp.status().is_success()); let req = test::TestRequest::get() .uri(&format!("/accounts/{}", nr2)) .to_request(); let resp = test::call_service(&app, req).await; assert!(resp.status().is_success()); let body_bytes = to_bytes(resp.into_body()).await.unwrap(); let response_json: Value = serde_json::from_slice(&body_bytes).unwrap(); let balance = response_json["balance"].as_f64().unwrap(); assert_eq!(5_f64, balance); let req = test::TestRequest::delete() .uri(&format!("/accounts/{}", nr)) .to_request(); let resp = test::call_service(&app, req).await; assert!(!resp.status().is_success()); let payload = AmountData { amount: 5_f64 }; let req = test::TestRequest::patch() .uri(&format!("/accounts/{}/withdraw", nr)) .set_form(&payload) .to_request(); let resp = test::call_service(&app, req).await; assert!(resp.status().is_success()); let req = test::TestRequest::get().uri("/accounts").to_request(); let resp = test::call_service(&app, req).await; assert!(resp.status().is_success()); let body_bytes = to_bytes(resp.into_body()).await.unwrap(); let response_json: Value = serde_json::from_slice(&body_bytes).unwrap(); let nrs = response_json.as_array().unwrap(); assert_eq!(2, nrs.len()); let req = test::TestRequest::delete() .uri(&format!("/accounts/{}", nr)) .to_request(); let resp = test::call_service(&app, req).await; assert!(resp.status().is_success()); let req = test::TestRequest::default() .method(HttpMethod::HEAD) .uri(&format!("/accounts/{}", nr)) .to_request(); let resp = test::call_service(&app, req).await; assert_eq!(resp.status(), StatusCode::GONE); let req = test::TestRequest::get().uri("/accounts").to_request(); let resp = test::call_service(&app, req).await; assert!(resp.status().is_success()); let body_bytes = to_bytes(resp.into_body()).await.unwrap(); let response_json: Value = serde_json::from_slice(&body_bytes).unwrap(); let nrs = response_json.as_array().unwrap(); assert_eq!(1, nrs.len()); } }