Create UserDao and unit tests for login
This commit is contained in:
@@ -1,72 +1,88 @@
|
||||
use bcrypt::{hash, verify, DEFAULT_COST};
|
||||
use diesel::prelude::*;
|
||||
use diesel::sqlite::SqliteConnection;
|
||||
use dotenv::dotenv;
|
||||
|
||||
use crate::database::models::{Favorite, InsertFavorite, InsertUser, User};
|
||||
|
||||
mod models;
|
||||
mod schema;
|
||||
|
||||
fn connect() -> SqliteConnection {
|
||||
dotenv().ok();
|
||||
|
||||
let db_url = dotenv::var("DATABASE_URL").expect("DATABASE_URL must be set");
|
||||
SqliteConnection::establish(&db_url).expect("Error connecting to DB")
|
||||
pub trait UserDao {
|
||||
fn create_user(&self, user: &str, password: &str) -> Option<User>;
|
||||
fn get_user(&self, user: &str, password: &str) -> Option<User>;
|
||||
fn user_exists(&self, user: &str) -> bool;
|
||||
}
|
||||
|
||||
// TODO: Should probably use Result here
|
||||
pub fn create_user(user: &str, pass: &str) -> Option<User> {
|
||||
use schema::users::dsl::*;
|
||||
pub struct SqliteUserDao {
|
||||
connection: SqliteConnection,
|
||||
}
|
||||
|
||||
let hashed = hash(pass, DEFAULT_COST);
|
||||
if let Ok(hash) = hashed {
|
||||
let connection = connect();
|
||||
diesel::insert_into(users)
|
||||
.values(InsertUser {
|
||||
username: user,
|
||||
password: &hash,
|
||||
})
|
||||
.execute(&connection)
|
||||
.unwrap();
|
||||
impl SqliteUserDao {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
connection: connect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UserDao for SqliteUserDao {
|
||||
// TODO: Should probably use Result here
|
||||
fn create_user(&self, user: &str, pass: &str) -> std::option::Option<User> {
|
||||
use schema::users::dsl::*;
|
||||
|
||||
let hashed = hash(pass, DEFAULT_COST);
|
||||
if let Ok(hash) = hashed {
|
||||
diesel::insert_into(users)
|
||||
.values(InsertUser {
|
||||
username: user,
|
||||
password: &hash,
|
||||
})
|
||||
.execute(&self.connection)
|
||||
.unwrap();
|
||||
|
||||
match users
|
||||
.filter(username.eq(username))
|
||||
.load::<User>(&self.connection)
|
||||
.unwrap()
|
||||
.first()
|
||||
{
|
||||
Some(u) => Some(u.clone()),
|
||||
None => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn get_user(&self, user: &str, pass: &str) -> Option<User> {
|
||||
use schema::users::dsl::*;
|
||||
|
||||
match users
|
||||
.filter(username.eq(user))
|
||||
.load::<User>(&connection)
|
||||
.unwrap()
|
||||
.load::<User>(&self.connection)
|
||||
.unwrap_or_default()
|
||||
.first()
|
||||
{
|
||||
Some(u) => Some(u.clone()),
|
||||
None => None,
|
||||
Some(u) if verify(pass, &u.password).unwrap_or(false) => Some(u.clone()),
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
||||
fn user_exists(&self, user: &str) -> bool {
|
||||
use schema::users::dsl::*;
|
||||
|
||||
users
|
||||
.filter(username.eq(user))
|
||||
.load::<User>(&self.connection)
|
||||
.unwrap_or_default()
|
||||
.first()
|
||||
.is_some()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_user(user: &str, pass: &str) -> Option<User> {
|
||||
use schema::users::dsl::*;
|
||||
|
||||
match users
|
||||
.filter(username.eq(user))
|
||||
.load::<User>(&connect())
|
||||
.unwrap_or_default()
|
||||
.first()
|
||||
{
|
||||
Some(u) if verify(pass, &u.password).unwrap_or(false) => Some(u.clone()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn user_exists(name: &str) -> bool {
|
||||
use schema::users::dsl::*;
|
||||
|
||||
users
|
||||
.filter(username.eq(name))
|
||||
.load::<User>(&connect())
|
||||
.unwrap_or_default()
|
||||
.first()
|
||||
.is_some()
|
||||
fn connect() -> SqliteConnection {
|
||||
let db_url = dotenv::var("DATABASE_URL").expect("DATABASE_URL must be set");
|
||||
SqliteConnection::establish(&db_url).expect("Error connecting to DB")
|
||||
}
|
||||
|
||||
pub fn add_favorite(user_id: i32, favorite_path: String) {
|
||||
@@ -90,3 +106,81 @@ pub fn get_favorites(user_id: i32) -> Vec<Favorite> {
|
||||
.load::<Favorite>(&connect())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod testhelpers {
|
||||
use actix_web::dev::{Body, ResponseBody};
|
||||
|
||||
use super::{models::User, UserDao};
|
||||
use std::cell::RefCell;
|
||||
use std::option::Option;
|
||||
|
||||
pub struct TestUserDao {
|
||||
pub user_map: RefCell<Vec<User>>,
|
||||
}
|
||||
|
||||
impl TestUserDao {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
user_map: RefCell::new(Vec::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UserDao for TestUserDao {
|
||||
fn create_user(&self, username: &str, password: &str) -> Option<User> {
|
||||
let u = User {
|
||||
id: (self.user_map.borrow().len() + 1) as i32,
|
||||
username: username.to_string(),
|
||||
password: password.to_string(),
|
||||
};
|
||||
|
||||
self.user_map.borrow_mut().push(u.clone());
|
||||
|
||||
Some(u)
|
||||
}
|
||||
|
||||
fn get_user(&self, user: &str, pass: &str) -> Option<User> {
|
||||
match self
|
||||
.user_map
|
||||
.borrow()
|
||||
.iter()
|
||||
.filter(|u| u.username == user && u.password == pass)
|
||||
.collect::<Vec<&User>>()
|
||||
.first()
|
||||
{
|
||||
Some(u) => {
|
||||
let copy = (*u).clone();
|
||||
Some(copy)
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn user_exists(&self, user: &str) -> bool {
|
||||
self.user_map
|
||||
.borrow()
|
||||
.iter()
|
||||
.filter(|u| u.username == user)
|
||||
.collect::<Vec<&User>>()
|
||||
.first()
|
||||
.is_some()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait BodyReader {
|
||||
fn read_to_str(&self) -> &str;
|
||||
}
|
||||
|
||||
impl BodyReader for ResponseBody<Body> {
|
||||
fn read_to_str(&self) -> &str {
|
||||
match self {
|
||||
ResponseBody::Body(body) => match body {
|
||||
Body::Bytes(b) => std::str::from_utf8(&b).unwrap(),
|
||||
_ => panic!("Unknown response body content"),
|
||||
},
|
||||
_ => panic!("Unknown response body"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user