From 26b76e42269a2217e8ef24c2cb723aed2f98bded Mon Sep 17 00:00:00 2001 From: Rohan Sircar Date: Wed, 5 May 2021 20:00:11 +0530 Subject: [PATCH] Add integration tests --- .env | 9 ++- .github/workflows/ci.yml | 16 ++-- Cargo.lock | 49 +++--------- Cargo.toml | 31 +++----- src/errors/domain_error.rs | 14 ++-- src/lib.rs | 96 ++++++++++++++++++++++ src/main.rs | 137 +++++++++----------------------- src/models/errors.rs | 8 +- src/routes/auth.rs | 12 +-- src/routes/users.rs | 22 +++-- tests/integration/common/mod.rs | 89 +++++++++++++++++++++ tests/integration/main.rs | 47 +++++++++++ 12 files changed, 335 insertions(+), 195 deletions(-) create mode 100644 src/lib.rs create mode 100644 tests/integration/common/mod.rs create mode 100644 tests/integration/main.rs diff --git a/.env b/.env index 4cc8d31..e6ec644 100644 --- a/.env +++ b/.env @@ -1,3 +1,8 @@ +#this is required for diesel_cli to work DATABASE_URL=data/app.db -BIND_ADDRESS=127.0.0.1:7800 -HASH_COST=8 +ACTIX_DEMO_DATABASE_URL=${DATABASE_URL} +ACTIX_DEMO_RUST_LOG=actix_demo=debug +ACTIX_DEMO_TEST_RUST_LOG=debug +ACTIX_DEMO_HTTP_HOST=127.0.0.1 +ACTIX_DEMO_HASH_COST=8 + diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 048e7e9..db07a8c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,11 +38,17 @@ jobs: override: true - name: Rust Cache uses: Swatinem/rust-cache@v1.2.0 - - - name: Run Tests + - name: Run Unit Tests uses: actions-rs/cargo@v1 with: command: test + args: --lib + - name: Run Integration Tests + uses: actions-rs/cargo@v1 + with: + command: test + args: --test integration + env: ACTIX_DEMO_TEST_RUST_LOG=warn lints: name: Lints @@ -99,7 +105,7 @@ jobs: use-cross: true command: build args: --release --target=aarch64-unknown-linux-gnu - + build-ppc: name: Build PowerPC Binaries if: github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')) @@ -132,7 +138,7 @@ jobs: with: command: build args: --release --target=powerpc64-unknown-linux-gnu - env: + env: RUSTFLAGS: "-C linker=powerpc64-linux-gnu-gcc-7" publish-docker: @@ -161,7 +167,7 @@ jobs: args: --release - name: Build Image run: docker build -f ci.Dockerfile -t rohansircar/actix-demo:latest . - env: + env: DOCKER_BUILDKIT: 1 - name: Publish Image run: docker push rohansircar/actix-demo:latest diff --git a/Cargo.lock b/Cargo.lock index e75162b..1af514f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,7 +40,9 @@ name = "actix-demo" version = "0.1.0" dependencies = [ "actix-files", + "actix-http", "actix-identity", + "actix-rt", "actix-service 2.0.0", "actix-threadpool", "actix-web", @@ -48,18 +50,16 @@ dependencies = [ "bcrypt", "bytes 1.0.1", "chrono", - "comp", "custom_error", "derive-new", "diesel", "diesel_migrations", "dotenv", "env_logger", + "envy", "futures", - "json", "lazy-regex", "lazy_static", - "listenfd", "log", "nanoid", "r2d2", @@ -69,7 +69,7 @@ dependencies = [ "serde", "serde_json", "timeago", - "uuid 0.8.2", + "uuid", "validator", "validator_derive", ] @@ -644,12 +644,6 @@ dependencies = [ "bitflags", ] -[[package]] -name = "comp" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7b6cae29f71a26f0dae0e291da438d6fced0e22e78aa1484cbbc085b5170949" - [[package]] name = "const_fn" version = "0.4.7" @@ -849,6 +843,15 @@ dependencies = [ "termcolor", ] +[[package]] +name = "envy" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f47e0157f2cb54f5ae1bd371b30a2ae4311e1c028f575cd4e81de7353215965" +dependencies = [ + "serde", +] + [[package]] name = "fallible-iterator" version = "0.2.0" @@ -1225,12 +1228,6 @@ version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" -[[package]] -name = "json" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd" - [[package]] name = "kernel32-sys" version = "0.2.2" @@ -1285,17 +1282,6 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" -[[package]] -name = "listenfd" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "492158e732f2e2de81c592f0a2427e57e12cd3d59877378fe7af624b6bbe0ca1" -dependencies = [ - "libc", - "uuid 0.6.5", - "winapi 0.3.9", -] - [[package]] name = "lock_api" version = "0.4.3" @@ -2486,15 +2472,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "uuid" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1436e58182935dcd9ce0add9ea0b558e8a87befe01c1a301e6020aeb0876363" -dependencies = [ - "cfg-if 0.1.10", -] - [[package]] name = "uuid" version = "0.8.2" diff --git a/Cargo.toml b/Cargo.toml index d07de34..53c68fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,16 +6,16 @@ edition = '2018' [dependencies] actix-web = "3.3.2" -# actix-rt = "1.1.1" actix-service = "2.0.0" actix-files = "0.5.0" +actix-http = "2.2.0" bytes = "1.0.1" futures = "0.3.14" log = "0.4.14" env_logger = "0.8.3" serde_json = "1.0.64" -json = "0.12.4" -listenfd = "0.3.3" +# json = "0.12.4" +# listenfd = "0.3.3" dotenv = "0.15.0" r2d2 = "0.8.9" validator = "0.13.0" @@ -27,7 +27,7 @@ rand = "0.8.3" nanoid = "0.4.0" bcrypt = "0.9.0" timeago = "0.3.0" -comp = "0.2.1" +# comp = "0.2.1" regex = "1.4.5" lazy_static = "1.4.0" lazy-regex = "0.1.4" @@ -35,29 +35,19 @@ custom_error = "1.9.2" derive-new = "0.5.9" diesel_migrations = "1.4.0" actix-threadpool = "0.3.3" +envy = "0.4" [dependencies.serde] version = "1.0.125" features = ['derive'] -# [dependencies.yarte] -# version = '0.9.0' -# features = ['html-min'] - [dependencies.diesel] version = "1.4.5" -features = [ - 'sqlite', - 'r2d2', - 'chrono', -] +features = ['sqlite', 'r2d2', 'chrono'] [dependencies.uuid] version = "0.8.2" -features = [ - 'serde', - 'v4', -] +features = ['serde', 'v4'] [dependencies.rusqlite] version = "0.23.1" @@ -66,7 +56,6 @@ features = ['bundled'] [dependencies.chrono] version = "0.4.19" features = ['serde'] -# [build-dependencies.yarte_helpers] -# version = '0.9.0' -# default-features = false -# features = ['config'] + +[dev-dependencies] +actix-rt = "1.1.1" diff --git a/src/errors/domain_error.rs b/src/errors/domain_error.rs index 496a121..30c25d1 100644 --- a/src/errors/domain_error.rs +++ b/src/errors/domain_error.rs @@ -40,7 +40,7 @@ impl ResponseError for DomainError { HttpResponse::InternalServerError().json(ErrorModel { // error_code: 500, success: false, - reason: err.to_string().as_str(), + reason: err.to_string(), }) } DomainError::DbError { source: _ } => { @@ -48,7 +48,7 @@ impl ResponseError for DomainError { HttpResponse::InternalServerError().json(ErrorModel { // error_code: 500, success: false, - reason: "Error in database", + reason: "Error in database".to_owned(), }) } DomainError::DbPoolError { source: _ } => { @@ -56,21 +56,21 @@ impl ResponseError for DomainError { HttpResponse::InternalServerError().json(ErrorModel { // error_code: 500, success: false, - reason: "Error getting database pool", + reason: "Error getting database pool".to_owned(), }) } DomainError::PasswordError { cause: _ } => { HttpResponse::BadRequest().json(ErrorModel { // error_code: 400, success: false, - reason: err.to_string().as_str(), + reason: err.to_string(), }) } DomainError::EntityDoesNotExistError { message: _ } => { HttpResponse::Accepted().json(ErrorModel { // error_code: 400, success: false, - reason: err.to_string().as_str(), + reason: err.to_string(), }) } DomainError::ThreadPoolError { message: _ } => { @@ -78,14 +78,14 @@ impl ResponseError for DomainError { HttpResponse::InternalServerError().json(ErrorModel { // error_code: 400, success: false, - reason: "Thread pool error occurred", + reason: "Thread pool error occurred".to_owned(), }) } DomainError::AuthError { message: _ } => { HttpResponse::Accepted().json(ErrorModel { // error_code: 400, success: false, - reason: err.to_string().as_str(), + reason: err.to_string(), }) } } diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..04e8f8e --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,96 @@ +#[macro_use] +extern crate diesel; +#[macro_use] +extern crate derive_new; +#[macro_use] +extern crate log; +extern crate bcrypt; +extern crate custom_error; +extern crate regex; +extern crate validator; + +mod actions; +mod errors; +mod middlewares; +pub mod models; +mod routes; +mod schema; +mod services; +mod types; +mod utils; + +use actix_files as fs; +use actix_identity::{CookieIdentityPolicy, IdentityService}; +use actix_web::{cookie::SameSite, middleware, web, App, HttpServer}; +use actix_web::{middleware::Logger, web::ServiceConfig}; +use rand::Rng; +use serde::Deserialize; +use types::DbPool; + +#[derive(Deserialize, Debug, Clone)] +pub struct EnvConfig { + pub database_url: String, + pub http_host: String, + #[serde(default = "default_hash_cost")] + pub hash_cost: u8, +} + +#[derive(Deserialize, Debug, Clone)] +pub struct AppConfig { + pub hash_cost: u8, +} + +#[derive(Clone)] +pub struct AppData { + pub config: AppConfig, + pub pool: DbPool, +} + +pub fn default_hash_cost() -> u8 { + 8 +} + +pub fn configure_app(app_data: AppData) -> Box { + Box::new(move |cfg: &mut ServiceConfig| { + cfg.data(app_data.clone()) + .service( + web::scope("/api") + .service(routes::users::get_user) + .service(routes::users::get_all_users), + ) + // .route("/api/users/get", web::get().to(user_controller.get_user.into())) + .service(web::scope("/api/public")) // public endpoint - not implemented yet + .service(routes::auth::login) + .service(routes::auth::logout) + .service(routes::auth::index) + .service(routes::users::add_user) + .service(fs::Files::new("/", "./static")); + }) +} + +pub fn id_service( + private_key: &[u8], +) -> actix_identity::IdentityService { + IdentityService::new( + CookieIdentityPolicy::new(&private_key) + .name("my-app-auth") + .secure(false) + .same_site(SameSite::Lax), + ) +} + +pub fn app_logger() -> Logger { + middleware::Logger::default() +} + +pub async fn run(addr: String, app_data: AppData) -> std::io::Result<()> { + info!("Starting server at {}", addr); + let private_key = rand::thread_rng().gen::<[u8; 32]>(); + let app = move || { + App::new() + .configure(configure_app(app_data.clone())) + .wrap(id_service(&private_key)) + .wrap(app_logger()) + }; + HttpServer::new(app).bind(addr)?.run().await +} diff --git a/src/main.rs b/src/main.rs index 6c4d26d..322cfd8 100755 --- a/src/main.rs +++ b/src/main.rs @@ -1,64 +1,38 @@ -#[macro_use] -extern crate diesel; -#[macro_use] -extern crate derive_new; -extern crate bcrypt; -extern crate custom_error; -extern crate regex; -extern crate validator; - -use actix_web::{cookie::SameSite, middleware, web, App, HttpServer}; - -use actix_files as fs; -use actix_identity::{CookieIdentityPolicy, IdentityService}; -use rand::Rng; - -use diesel::prelude::*; -use diesel::r2d2::{self, ConnectionManager}; +use actix_demo::{AppConfig, AppData, EnvConfig}; +use diesel::{r2d2::ConnectionManager, SqliteConnection}; +use env_logger::Env; +use io::ErrorKind; use std::io; -use std::io::ErrorKind; -use types::DbPool; - -mod actions; -mod errors; -mod middlewares; -mod models; -mod routes; -mod schema; -mod services; -mod types; -mod utils; - -#[macro_use] -extern crate log; - -#[derive(Clone)] -pub struct AppConfig { - hash_cost: u32, - pool: DbPool, -} #[actix_web::main] -async fn main() -> std::io::Result<()> { - std::env::set_var("RUST_LOG", "debug"); - env_logger::init(); - dotenv::dotenv().map_err(|err| { +async fn main() -> io::Result<()> { + let _ = dotenv::dotenv().map_err(|err| { io::Error::new( ErrorKind::Other, format!("Failed to set up env: {:?}", err), ) })?; - // let _basic_auth_middleware = - // HttpAuthentication::basic(utils::auth::validator); - - // set up database connection pool - let connspec = std::env::var("DATABASE_URL").map_err(|err| { + let _ = env_logger::try_init_from_env( + Env::default().filter("ACTIX_DEMO_RUST_LOG"), + ) + .map_err(|err| { io::Error::new( ErrorKind::Other, - format!("Database url is not set: {:?}", err), + format!("Failed to set up env logger: {:?}", err), ) })?; + + let env_config = envy::prefixed("ACTIX_DEMO_") + .from_env::() + .map_err(|err| { + io::Error::new( + ErrorKind::Other, + format!("Failed to parse config: {:?}", err), + ) + })?; + + let connspec = &env_config.database_url; let manager = ConnectionManager::::new(connspec); let pool = r2d2::Pool::builder().build(manager).map_err(|err| { io::Error::new( @@ -67,7 +41,7 @@ async fn main() -> std::io::Result<()> { ) })?; - { + let _ = { let conn = &pool.get().map_err(|err| { io::Error::new( ErrorKind::Other, @@ -75,60 +49,21 @@ async fn main() -> std::io::Result<()> { ) })?; - diesel_migrations::run_pending_migrations(conn).map_err(|err| { - io::Error::new( - ErrorKind::Other, - format!("Error running migrations: {:?}", err), - ) - })?; - } - - let hash_cost = std::env::var("HASH_COST") - .map_err(|e| e.to_string()) - .and_then(|x| x.parse::().map_err(|e| e.to_string())) - .unwrap_or_else(|err| { - info!( - "Error getting hash cost: {:?}. Using default cost of 8", - err - ); - 8 - }); + let _ = + diesel_migrations::run_pending_migrations(conn).map_err(|err| { + io::Error::new( + ErrorKind::Other, + format!("Error running migrations: {:?}", err), + ) + })?; + }; - let config: AppConfig = AppConfig { + let app_data = AppData { + config: AppConfig { + hash_cost: env_config.hash_cost, + }, pool: pool.clone(), - hash_cost, }; - // let user_controller = UserController { - // user_service: &user_service, - // }; - - let addr = std::env::var("BIND_ADDRESS") - .unwrap_or_else(|_| "127.0.0.1:7800".to_owned()); - info!("Starting server at {}", addr); - let private_key = rand::thread_rng().gen::<[u8; 32]>(); - let app = move || { - App::new() - .data(config.clone()) - .wrap(IdentityService::new( - CookieIdentityPolicy::new(&private_key) - .name("my-app-auth") - .secure(false) - .same_site(SameSite::Lax), - )) - .wrap(middleware::Logger::default()) - .service( - web::scope("/api") - .service(routes::users::get_user) - .service(routes::users::get_all_users), - ) - // .route("/api/users/get", web::get().to(user_controller.get_user.into())) - .service(web::scope("/api/public")) // public endpoint - not implemented yet - .service(routes::auth::login) - .service(routes::auth::logout) - .service(routes::auth::index) - .service(routes::users::add_user) - .service(fs::Files::new("/", "./static")) - }; - HttpServer::new(app).bind(addr)?.run().await + actix_demo::run(format!("{}:7800", env_config.http_host), app_data).await } diff --git a/src/models/errors.rs b/src/models/errors.rs index 0cdd835..de6b5b5 100644 --- a/src/models/errors.rs +++ b/src/models/errors.rs @@ -1,4 +1,4 @@ -use serde::Serialize; +use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Serialize, new)] pub struct JsonErrorModel<'a> { @@ -6,9 +6,9 @@ pub struct JsonErrorModel<'a> { pub line: String, pub reason: &'a str, } -#[derive(Debug, Clone, Serialize, new)] -pub struct ErrorModel<'a> { +#[derive(PartialEq, Debug, Clone, Serialize, Deserialize, new)] +pub struct ErrorModel { // pub error_code: i16, pub success: bool, - pub reason: &'a str, + pub reason: String, } diff --git a/src/routes/auth.rs b/src/routes/auth.rs index a07a8ea..d69fa48 100644 --- a/src/routes/auth.rs +++ b/src/routes/auth.rs @@ -1,8 +1,8 @@ use actix_web::web; use actix_web_httpauth::extractors::basic::BasicAuth; -use crate::actions::users; -use crate::{errors::DomainError, AppConfig}; +use crate::errors::DomainError; +use crate::{actions::users, AppData}; use actix_identity::Identity; use actix_web::{get, Error, HttpResponse}; @@ -10,7 +10,7 @@ use actix_web::{get, Error, HttpResponse}; pub async fn login( id: Identity, credentials: BasicAuth, - config: web::Data, + app_data: web::Data, ) -> Result { let maybe_identity = id.identity(); let response = if let Some(identity) = maybe_identity { @@ -21,7 +21,7 @@ pub async fn login( } else { let credentials2 = credentials.clone(); let valid = - web::block(move || validate_basic_auth(credentials2, &config)) + web::block(move || validate_basic_auth(credentials2, &app_data)) .await .map_err(|_err| { DomainError::new_thread_pool_error(_err.to_string()) @@ -68,10 +68,10 @@ pub async fn index(id: Identity) -> String { /// basic auth middleware function pub fn validate_basic_auth( credentials: BasicAuth, - config: &AppConfig, + app_data: &AppData, ) -> Result { let result = if let Some(password_ref) = credentials.password() { - let pool = &config.pool; + let pool = &app_data.pool; let conn = pool.get()?; let password = password_ref.clone().into_owned(); let valid = users::verify_password( diff --git a/src/routes/users.rs b/src/routes/users.rs index 6abb1d3..84ebcfc 100644 --- a/src/routes/users.rs +++ b/src/routes/users.rs @@ -1,29 +1,26 @@ use actix_web::{get, post, web, HttpResponse}; -use crate::errors::DomainError; use crate::services::UserService; -use crate::utils::LogErrorResult; -use crate::AppConfig; use crate::{actions, models}; +use crate::{errors::DomainError, AppData}; use actix_web::error::ResponseError; use validator::Validate; /// Finds user by UID. #[get("/get/users/{user_id}")] pub async fn get_user( - config: web::Data, + app_data: web::Data, user_id_param: web::Path, ) -> Result { let u_id = user_id_param.into_inner(); // use web::block to offload blocking Diesel code without blocking server thread let res = web::block(move || { - let pool = &config.pool; + let pool = &app_data.pool; let conn = pool.get()?; actions::find_user_by_uid(u_id, &conn) }) .await - .map_err(|err| DomainError::new_thread_pool_error(err.to_string())) - .log_err()?; + .map_err(|err| DomainError::new_thread_pool_error(err.to_string()))?; if let Some(user) = res { Ok(HttpResponse::Ok().json(user)) } else { @@ -55,17 +52,16 @@ pub async fn get_user2( #[get("/get/users")] pub async fn get_all_users( - config: web::Data, + app_data: web::Data, ) -> Result { // use web::block to offload blocking Diesel code without blocking server thread let users = web::block(move || { - let pool = &config.pool; + let pool = &app_data.pool; let conn = pool.get()?; actions::get_all(&conn) }) .await - .map_err(|err| DomainError::new_thread_pool_error(err.to_string())) - .log_err()?; + .map_err(|err| DomainError::new_thread_pool_error(err.to_string()))?; debug!("{:?}", users); @@ -81,13 +77,13 @@ pub async fn get_all_users( /// Inserts new user with name defined in form. #[post("/do_registration")] pub async fn add_user( - config: web::Data, + app_data: web::Data, form: web::Json, ) -> Result { // use web::block to offload blocking Diesel code without blocking server thread let res = match form.0.validate() { Ok(_) => web::block(move || { - let pool = &config.pool; + let pool = &app_data.pool; let conn = pool.get()?; actions::insert_new_user(form.0, &conn) }) diff --git a/tests/integration/common/mod.rs b/tests/integration/common/mod.rs new file mode 100644 index 0000000..1cd7d3c --- /dev/null +++ b/tests/integration/common/mod.rs @@ -0,0 +1,89 @@ +extern crate actix_demo; +use actix_demo::{AppConfig, AppData}; +use actix_web::test; +use actix_web::App; +use diesel::SqliteConnection; + +use diesel::r2d2::{self, ConnectionManager}; +use env_logger::Env; +use std::io; +use std::io::ErrorKind; + +use actix_demo::configure_app; + +use actix_http::Request; +use actix_web::{dev as ax_dev, Error as AxError}; + +pub async fn test_app() -> impl ax_dev::Service< + Request = Request, + Response = ax_dev::ServiceResponse, + Error = AxError, +> { + let _ = dotenv::dotenv() + .map_err(|err| { + io::Error::new( + ErrorKind::Other, + format!("Failed to set up env: {:?}", err), + ) + }) + .unwrap(); + let _ = env_logger::builder() + .is_test(true) + .parse_env(Env::default().filter("ACTIX_DEMO_TEST_RUST_LOG")) + .try_init(); + + let connspec = ":memory:"; + let manager = ConnectionManager::::new(connspec); + let pool = r2d2::Pool::builder() + .build(manager) + .map_err(|err| { + io::Error::new( + ErrorKind::Other, + format!("Failed to create pool: {:?}", err), + ) + }) + .unwrap(); + + let _ = { + let conn = &pool + .get() + .map_err(|err| { + io::Error::new( + ErrorKind::Other, + format!("Failed to get connection: {:?}", err), + ) + }) + .unwrap(); + + let migrations_dir = diesel_migrations::find_migrations_directory() + .map_err(|err| { + io::Error::new( + ErrorKind::Other, + format!("Error finding migrations dir: {:?}", err), + ) + }) + .unwrap(); + let _ = diesel_migrations::run_pending_migrations_in_directory( + conn, + &migrations_dir, + &mut io::sink(), + ) + .map_err(|err| { + io::Error::new( + ErrorKind::Other, + format!("Error running migrations: {:?}", err), + ) + }) + .unwrap(); + }; + + test::init_service( + App::new() + .configure(configure_app(AppData { + config: AppConfig { hash_cost: 8 }, + pool, + })) + .wrap(actix_web::middleware::Logger::default()), + ) + .await +} diff --git a/tests/integration/main.rs b/tests/integration/main.rs new file mode 100644 index 0000000..a08e70f --- /dev/null +++ b/tests/integration/main.rs @@ -0,0 +1,47 @@ +mod common; + +#[cfg(test)] +mod tests { + + use super::*; + extern crate actix_demo; + use actix_demo::models::ErrorModel; + use actix_web::dev::Service as _; + use actix_web::http::StatusCode; + use actix_web::test; + + #[actix_rt::test] + async fn get_users_api_should_succeed() { + let req = test::TestRequest::get().uri("/api/get/users").to_request(); + let resp = common::test_app().await.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::ACCEPTED); + let body: ErrorModel = test::read_body_json(resp).await; + assert_eq!( + body, + ErrorModel { + success: false, + reason: "Entity does not exist - No users available".to_owned() + } + ); + log::debug!("{:?}", body); + } + + #[actix_rt::test] + async fn get_user_api_should_succeed() { + let req = test::TestRequest::get() + .uri("/api/get/users/1") + .to_request(); + let resp = common::test_app().await.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::ACCEPTED); + let body: ErrorModel = test::read_body_json(resp).await; + assert_eq!( + body, + ErrorModel { + success: false, + reason: "Entity does not exist - No user found with uid: 1" + .to_owned() + } + ); + log::debug!("{:?}", body); + } +}