diff --git a/server/src/main.rs b/server/src/main.rs index 2623638..c321a34 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -10,6 +10,9 @@ mod errors; mod schema; mod user; +#[cfg(test)] +mod test_helpers; + pub type Pool = r2d2::Pool>; #[actix_web::main] diff --git a/server/src/test_helpers.rs b/server/src/test_helpers.rs new file mode 100644 index 0000000..6347134 --- /dev/null +++ b/server/src/test_helpers.rs @@ -0,0 +1,8 @@ +use diesel::prelude::*; + +pub fn connection() -> PgConnection { + let url = dotenv::var("DATABASE_URL").expect("DATABASE_URL must be set"); + let conn = PgConnection::establish(&url).unwrap(); + conn.begin_test_transaction().unwrap(); + conn +} diff --git a/server/src/user/model.rs b/server/src/user/model.rs index 561cc48..f0cc42b 100644 --- a/server/src/user/model.rs +++ b/server/src/user/model.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; use crate::schema::users; -#[derive(Serialize, Deserialize, Queryable, Insertable)] +#[derive(Serialize, Deserialize, Queryable, Insertable, PartialEq, Debug)] #[table_name = "users"] pub struct User { pub id: i32, @@ -11,7 +11,7 @@ pub struct User { pub password: String, } // decode request data -#[derive(Deserialize, Insertable, AsChangeset, Debug)] +#[derive(Clone, Deserialize, Insertable, AsChangeset, Debug)] #[table_name = "users"] pub struct UserData { pub username: String, @@ -64,3 +64,114 @@ impl User { Ok(count) } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::test_helpers::*; + + fn create_user_data() -> UserData { + UserData { + username: String::from("username"), + password: String::from("password"), + } + } + + fn setup_user(conn: &PgConnection) -> User { + User::create(create_user_data(), conn).unwrap() + } + + #[test] + fn create_returns_new_user() { + let conn = connection(); + + let user_data = create_user_data(); + let user = User::create(user_data.clone(), &conn).unwrap(); + assert_eq!(user.username, user_data.username); + assert_eq!(user.password, user_data.password); + } + + #[test] + fn find_returns_none_when_no_user_exists() { + let conn = connection(); + + assert!(matches!(User::find(&conn, 42), Ok(None))); + } + + #[test] + fn find_returns_user_when_exists() { + let conn = connection(); + + let expected = setup_user(&conn); + let user = User::find(&conn, expected.id).unwrap(); + + assert_eq!(Some(expected), user); + } + + #[test] + fn find_all_returns_empty_list_when_no_users_exist() { + let conn = connection(); + + assert_eq!(User::find_all(&conn).unwrap().len(), 0); + } + + #[test] + fn find_all_returns_all_users() { + let conn = connection(); + + setup_user(&conn); + setup_user(&conn); + + let users = User::find_all(&conn).unwrap(); + + assert_eq!(users.len(), 2); + assert_ne!(users[0].id, users[1].id); + } + + #[test] + fn update_fails_with_not_found_if_user_does_not_exist() { + let conn = connection(); + + let user = create_user_data(); + + let user = User::update(42, user, &conn); + assert!(matches!(user, Err(diesel::result::Error::NotFound))); + } + + #[test] + fn update_returns_updated_user_if_exists() { + let conn = connection(); + + let mut user = setup_user(&conn); + let update_user = UserData { + username: String::from("new_username"), + password: String::from("new_password"), + }; + + // Update user manually + user.username = update_user.username.clone(); + user.password = update_user.password.clone(); + + let updated_user = User::update(user.id, update_user, &conn).unwrap(); + + assert_eq!(updated_user, user); + } + + #[test] + fn destroy_returns_null_if_user_does_not_exist() { + let conn = connection(); + + let count = User::destroy(&conn, 42).unwrap(); + assert_eq!(count, 0); + } + + #[test] + fn destroy_returns_count_if_user_exists() { + let conn = connection(); + + let user = setup_user(&conn); + + let count = User::destroy(&conn, user.id).unwrap(); + assert_eq!(count, 1); + } +}