Create UserDao and unit tests for login

This commit is contained in:
Cameron Cordes
2021-02-26 08:33:02 -05:00
parent 64bfb58734
commit e5ad88abd6
3 changed files with 214 additions and 59 deletions

View File

@@ -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"),
}
}
}
}