Add integration tests
This commit is contained in:
parent
eb9e97c3aa
commit
26b76e4226
9
.env
9
.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
|
||||
|
||||
|
10
.github/workflows/ci.yml
vendored
10
.github/workflows/ci.yml
vendored
@ -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
|
||||
|
49
Cargo.lock
generated
49
Cargo.lock
generated
@ -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"
|
||||
|
31
Cargo.toml
31
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"
|
||||
|
@ -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(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
96
src/lib.rs
Normal file
96
src/lib.rs
Normal file
@ -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<dyn Fn(&mut ServiceConfig)> {
|
||||
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<CookieIdentityPolicy> {
|
||||
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
|
||||
}
|
125
src/main.rs
125
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::<EnvConfig>()
|
||||
.map_err(|err| {
|
||||
io::Error::new(
|
||||
ErrorKind::Other,
|
||||
format!("Failed to parse config: {:?}", err),
|
||||
)
|
||||
})?;
|
||||
|
||||
let connspec = &env_config.database_url;
|
||||
let manager = ConnectionManager::<SqliteConnection>::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<()> {
|
||||
)
|
||||
})?;
|
||||
|
||||
let _ =
|
||||
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::<u32>().map_err(|e| e.to_string()))
|
||||
.unwrap_or_else(|err| {
|
||||
info!(
|
||||
"Error getting hash cost: {:?}. Using default cost of 8",
|
||||
err
|
||||
);
|
||||
8
|
||||
});
|
||||
|
||||
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
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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<AppConfig>,
|
||||
app_data: web::Data<AppData>,
|
||||
) -> Result<HttpResponse, DomainError> {
|
||||
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<bool, DomainError> {
|
||||
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(
|
||||
|
@ -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<AppConfig>,
|
||||
app_data: web::Data<AppData>,
|
||||
user_id_param: web::Path<i32>,
|
||||
) -> Result<HttpResponse, DomainError> {
|
||||
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<AppConfig>,
|
||||
app_data: web::Data<AppData>,
|
||||
) -> Result<HttpResponse, DomainError> {
|
||||
// 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<AppConfig>,
|
||||
app_data: web::Data<AppData>,
|
||||
form: web::Json<models::NewUser>,
|
||||
) -> Result<HttpResponse, impl ResponseError> {
|
||||
// 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)
|
||||
})
|
||||
|
89
tests/integration/common/mod.rs
Normal file
89
tests/integration/common/mod.rs
Normal file
@ -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<impl ax_dev::MessageBody>,
|
||||
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::<SqliteConnection>::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
|
||||
}
|
47
tests/integration/main.rs
Normal file
47
tests/integration/main.rs
Normal file
@ -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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user