From 0f0d7f488f3a63157c40809a94e98cb223cad413 Mon Sep 17 00:00:00 2001 From: Joe Ardent Date: Sat, 3 Jun 2023 10:11:07 -0700 Subject: [PATCH 1/6] Add more test utils. --- src/login.rs | 16 ++++++++-------- src/test_utils.rs | 38 +++++++++++++++++++++++++++++++++++--- 2 files changed, 43 insertions(+), 11 deletions(-) diff --git a/src/login.rs b/src/login.rs index 98aae5b..64c0328 100644 --- a/src/login.rs +++ b/src/login.rs @@ -107,14 +107,14 @@ mod test { use crate::{ templates::{Index, LoginGet, LogoutGet, LogoutPost}, - test_utils::{get_user, tserver}, + test_utils::{get_user, server}, }; const LOGIN_FORM: &str = "username=test_user&password=a"; #[tokio::test] async fn get_login() { - let s = tserver().await; + let s = server().await; let resp = s.get("/login").await; let body = std::str::from_utf8(resp.bytes()).unwrap().to_string(); assert_eq!(body, LoginGet::default().to_string()); @@ -122,7 +122,7 @@ mod test { #[tokio::test] async fn post_login_success() { - let s = tserver().await; + let s = server().await; let form = LOGIN_FORM.to_string(); let bytes = form.as_bytes(); @@ -139,7 +139,7 @@ mod test { #[tokio::test] async fn post_login_bad_user() { - let s = tserver().await; + let s = server().await; let form = "username=test_LOSER&password=aaaa".to_string(); let bytes = form.as_bytes(); @@ -156,7 +156,7 @@ mod test { #[tokio::test] async fn post_login_bad_password() { - let s = tserver().await; + let s = server().await; let form = "username=test_user&password=bbbb".to_string(); let bytes = form.as_bytes(); @@ -173,7 +173,7 @@ mod test { #[tokio::test] async fn get_logout() { - let s = tserver().await; + let s = server().await; let resp = s.get("/logout").await; let body = std::str::from_utf8(resp.bytes()).unwrap().to_string(); assert_eq!(body, LogoutGet.to_string()); @@ -181,7 +181,7 @@ mod test { #[tokio::test] async fn post_logout_not_logged_in() { - let s = tserver().await; + let s = server().await; let resp = s.post("/logout").await; resp.assert_status_ok(); let body = std::str::from_utf8(resp.bytes()).unwrap(); @@ -191,7 +191,7 @@ mod test { #[tokio::test] async fn post_logout_logged_in() { - let s = tserver().await; + let s = server().await; // log in and prove it { diff --git a/src/test_utils.rs b/src/test_utils.rs index fc103ec..f6714f1 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -1,18 +1,21 @@ use axum_test::{TestServer, TestServerConfig}; +use sqlx::SqlitePool; use uuid::Uuid; use crate::User; pub fn get_user() -> User { User { - username: "test_user".to_string(), - pwhash: "$argon2id$v=19$m=19456,t=2,p=1$GWsCH1w5RYaP9WWmq+xw0g$hmOEqC+MU+vnEk3bOdkoE+z01mOmmOeX08XyPyjqua8".to_string(), + username: "test_user".to_string(), + // corresponding to a password of "a": + pwhash: "$argon2id$v=19$m=19456,t=2,p=1$GWsCH1w5RYaP9WWmq+xw0g$hmOEqC+MU+vnEk3bOdkoE+z01mOmmOeX08XyPyjqua8".to_string(), id: Uuid::nil(), + displayname: Some("Test User".to_string()), ..Default::default() } } -pub async fn tserver() -> TestServer { +pub async fn server() -> TestServer { let pool = crate::db::get_pool().await; let secret = [0u8; 64]; @@ -40,3 +43,32 @@ pub async fn tserver() -> TestServer { }; TestServer::new_with_config(app, config).unwrap() } + +pub async fn server_with_pool(pool: &SqlitePool) -> TestServer { + let secret = [0u8; 64]; + + let r = sqlx::query("select count(*) from witches") + .fetch_one(pool) + .await; + assert!(r.is_ok()); + + let app = crate::app(pool.clone(), &secret).await.into_make_service(); + + let config = TestServerConfig { + save_cookies: true, + ..Default::default() + }; + TestServer::new_with_config(app, config).unwrap() +} + +pub async fn insert_user(user: &User, pool: &SqlitePool) { + sqlx::query(crate::signup::CREATE_QUERY) + .bind(user.id) + .bind(&user.username) + .bind(&user.displayname) + .bind(&user.email) + .bind(&user.pwhash) + .execute(pool) + .await + .unwrap(); +} From bad698cc193df42c87ca1156ed54ff9df1353a0c Mon Sep 17 00:00:00 2001 From: Joe Ardent Date: Sat, 3 Jun 2023 10:42:23 -0700 Subject: [PATCH 2/6] add skeleton for signup tests --- src/signup.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/signup.rs b/src/signup.rs index eac1918..818cfc1 100644 --- a/src/signup.rs +++ b/src/signup.rs @@ -228,3 +228,33 @@ pub(crate) async fn create_user( _ => Err(CreateUserErrorKind::UnknownDBError.into()), } } + +#[cfg(test)] +mod test { + use crate::{ + db::get_pool, + test_utils::{get_user, server_with_pool}, + }; + + const GOOD_FORM: &str = "username=test_user&displayname=Test+User&password=aaaa&pw_verify=aaaa"; + + // various ways to fuck up signup + const MISMATCH_PW_FORM: &str = + "username=test_user&displayname=Test+User&password=aaaa&pw_verify=bbbb"; + const SHORT_PW_FORM: &str = "username=test_user&displayname=Test+User&password=a&pw_verify=a"; + const LONG_USERNAME_FORM: &str = "username=test_user12345678901234567890&displayname=Test+User& + password=aaaa&pw_verify=aaaa"; + + #[tokio::test] + async fn post_create_user_success() { + todo!() + } + + async fn get_create_user() { + todo!() + } + + async fn handle_signup_success() { + todo!() + } +} From 09a07f2c5711e6d4b43b4fd3fec5fae4ebceaf2d Mon Sep 17 00:00:00 2001 From: Joe Ardent Date: Sat, 3 Jun 2023 12:36:18 -0700 Subject: [PATCH 3/6] more test skeletons --- src/signup.rs | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/src/signup.rs b/src/signup.rs index 818cfc1..986f172 100644 --- a/src/signup.rs +++ b/src/signup.rs @@ -238,22 +238,43 @@ mod test { const GOOD_FORM: &str = "username=test_user&displayname=Test+User&password=aaaa&pw_verify=aaaa"; - // various ways to fuck up signup - const MISMATCH_PW_FORM: &str = - "username=test_user&displayname=Test+User&password=aaaa&pw_verify=bbbb"; - const SHORT_PW_FORM: &str = "username=test_user&displayname=Test+User&password=a&pw_verify=a"; - const LONG_USERNAME_FORM: &str = "username=test_user12345678901234567890&displayname=Test+User& - password=aaaa&pw_verify=aaaa"; - #[tokio::test] async fn post_create_user_success() { todo!() } + mod failure { + // various ways to fuck up signup + const MISMATCH_PW_FORM: &str = + "username=test_user&displayname=Test+User&password=aaaa&pw_verify=bbbb"; + const SHORT_PW_FORM: &str = + "username=test_user&displayname=Test+User&password=a&pw_verify=a"; + const LONG_USERNAME_FORM: &str = + "username=test_user12345678901234567890&displayname=Test+User& + password=aaaa&pw_verify=aaaa"; + + #[tokio::test] + async fn mismatch_pw() { + todo!() + } + + #[tokio::test] + async fn too_short_pw() { + todo!() + } + + #[tokio::test] + async fn too_long_username() { + todo!() + } + } + + #[tokio::test] async fn get_create_user() { todo!() } + #[tokio::test] async fn handle_signup_success() { todo!() } From 38a6cdeb84feb442342fe9a8524e2f20a709ce56 Mon Sep 17 00:00:00 2001 From: Joe Ardent Date: Sat, 3 Jun 2023 13:58:37 -0700 Subject: [PATCH 4/6] backport test utils improvments to login tests --- src/login.rs | 29 +++++++++++------------------ src/test_utils.rs | 8 ++++++++ 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/login.rs b/src/login.rs index 64c0328..9d597ee 100644 --- a/src/login.rs +++ b/src/login.rs @@ -107,7 +107,7 @@ mod test { use crate::{ templates::{Index, LoginGet, LogoutGet, LogoutPost}, - test_utils::{get_user, server}, + test_utils::{get_user, massage, server, FORM_CONTENT_TYPE}, }; const LOGIN_FORM: &str = "username=test_user&password=a"; @@ -124,14 +124,12 @@ mod test { async fn post_login_success() { let s = server().await; - let form = LOGIN_FORM.to_string(); - let bytes = form.as_bytes(); - let body = Bytes::copy_from_slice(bytes); + let body = massage(LOGIN_FORM); let resp = s .post("/login") .expect_failure() - .content_type("application/x-www-form-urlencoded") + .content_type(FORM_CONTENT_TYPE) .bytes(body) .await; assert_eq!(resp.status_code(), 303); @@ -141,14 +139,13 @@ mod test { async fn post_login_bad_user() { let s = server().await; - let form = "username=test_LOSER&password=aaaa".to_string(); - let bytes = form.as_bytes(); - let body = Bytes::copy_from_slice(bytes); + let form = "username=test_LOSER&password=aaaa"; + let body = massage(form); let resp = s .post("/login") .expect_success() - .content_type("application/x-www-form-urlencoded") + .content_type(FORM_CONTENT_TYPE) .bytes(body) .await; assert_eq!(resp.status_code(), 200); @@ -158,14 +155,13 @@ mod test { async fn post_login_bad_password() { let s = server().await; - let form = "username=test_user&password=bbbb".to_string(); - let bytes = form.as_bytes(); - let body = Bytes::copy_from_slice(bytes); + let form = "username=test_user&password=bbbb"; + let body = massage(form); let resp = s .post("/login") .expect_success() - .content_type("application/x-www-form-urlencoded") + .content_type(FORM_CONTENT_TYPE) .bytes(body) .await; assert_eq!(resp.status_code(), 200); @@ -195,14 +191,11 @@ mod test { // log in and prove it { - let form = LOGIN_FORM.to_string(); - let bytes = form.as_bytes(); - let body = Bytes::copy_from_slice(bytes); - + let body = massage(LOGIN_FORM); let resp = s .post("/login") .expect_failure() - .content_type("application/x-www-form-urlencoded") + .content_type(FORM_CONTENT_TYPE) .bytes(body) .await; assert_eq!(resp.status_code(), 303); diff --git a/src/test_utils.rs b/src/test_utils.rs index f6714f1..3f6559e 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -1,9 +1,12 @@ +use axum::body::Bytes; use axum_test::{TestServer, TestServerConfig}; use sqlx::SqlitePool; use uuid::Uuid; use crate::User; +pub const FORM_CONTENT_TYPE: &str = "application/x-www-form-urlencoded"; + pub fn get_user() -> User { User { username: "test_user".to_string(), @@ -72,3 +75,8 @@ pub async fn insert_user(user: &User, pool: &SqlitePool) { .await .unwrap(); } + +// https://www.youtube.com/watch?v=29MJySO7PGg +pub fn massage(s: &str) -> Bytes { + Bytes::from_iter(s.chars().map(|c| c as u8)) +} From 8d040ad36899d0dedc0a645b9531e4d80ecea88d Mon Sep 17 00:00:00 2001 From: Joe Ardent Date: Sat, 3 Jun 2023 15:45:19 -0700 Subject: [PATCH 5/6] Finish signup tests. --- src/lib.rs | 5 +- src/login.rs | 6 +- src/signup.rs | 262 +++++++++++++++++++++++++++++++++++++++------- src/templates.rs | 4 + src/test_utils.rs | 4 +- 5 files changed, 233 insertions(+), 48 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 01afc36..6638c22 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,10 +30,7 @@ pub async fn app(db_pool: SqlitePool, secret: &[u8]) -> Router { Router::new() .route("/", get(handle_slash).post(handle_slash)) .route("/signup", get(get_create_user).post(post_create_user)) - .route( - "/signup_success/:id", - get(handle_signup_success).post(handle_signup_success), - ) + .route("/signup_success/:id", get(handle_signup_success)) .route("/login", get(get_login).post(post_login)) .route("/logout", get(get_logout).post(post_logout)) .fallback(handle_slash_redir) diff --git a/src/login.rs b/src/login.rs index 9d597ee..f9532f2 100644 --- a/src/login.rs +++ b/src/login.rs @@ -103,11 +103,9 @@ pub async fn post_logout(mut auth: AuthContext) -> impl IntoResponse { #[cfg(test)] mod test { - use axum::body::Bytes; - use crate::{ templates::{Index, LoginGet, LogoutGet, LogoutPost}, - test_utils::{get_user, massage, server, FORM_CONTENT_TYPE}, + test_utils::{get_test_user, massage, server, FORM_CONTENT_TYPE}, }; const LOGIN_FORM: &str = "username=test_user&password=a"; @@ -201,7 +199,7 @@ mod test { assert_eq!(resp.status_code(), 303); let logged_in = Index { - user: Some(get_user()), + user: Some(get_test_user()), } .to_string(); diff --git a/src/signup.rs b/src/signup.rs index 986f172..02bb93f 100644 --- a/src/signup.rs +++ b/src/signup.rs @@ -2,7 +2,6 @@ use argon2::{ password_hash::{rand_core::OsRng, PasswordHasher, SaltString}, Argon2, }; -use askama::Template; use axum::{ extract::{Form, Path, State}, http::StatusCode, @@ -12,7 +11,10 @@ use sqlx::{query_as, SqlitePool}; use unicode_segmentation::UnicodeSegmentation; use uuid::Uuid; -use crate::{templates::CreateUser, User}; +use crate::{ + templates::{CreateUser, CreateUserSuccess}, + User, +}; pub(crate) const CREATE_QUERY: &str = "insert into witches (id, username, displayname, email, pwhash) values ($1, $2, $3, $4, $5)"; @@ -22,10 +24,6 @@ const ID_QUERY: &str = "select * from witches where id = $1"; // Result types for user creation //-************************************************************************ -#[derive(Debug, Clone, Template)] -#[template(path = "signup_success.html")] -pub struct CreateUserSuccess(User); - #[Error(desc = "Could not create user.")] #[non_exhaustive] pub struct CreateUserError(#[from] CreateUserErrorKind); @@ -36,7 +34,7 @@ impl IntoResponse for CreateUserError { CreateUserErrorKind::UnknownDBError => { (StatusCode::INTERNAL_SERVER_ERROR, format!("{self}")).into_response() } - _ => (StatusCode::BAD_REQUEST, format!("{self}")).into_response(), + _ => (StatusCode::OK, format!("{self}")).into_response(), } } } @@ -133,7 +131,7 @@ pub async fn post_create_user( let id = user.id.as_simple().to_string(); let location = format!("/signup_success/{id}"); - let resp = axum::response::Redirect::temporary(&location); + let resp = axum::response::Redirect::to(&location); Ok(resp) } @@ -157,7 +155,7 @@ pub async fn handle_signup_success( if user.username.is_empty() || id.is_empty() { // redirect to front page if we got here without a valid witch ID - *resp.status_mut() = StatusCode::TEMPORARY_REDIRECT; + *resp.status_mut() = StatusCode::SEE_OTHER; resp.headers_mut().insert("Location", "/".parse().unwrap()); } @@ -229,53 +227,241 @@ pub(crate) async fn create_user( } } +//-************************************************************************ +// TESTS +//-************************************************************************ + #[cfg(test)] mod test { use crate::{ db::get_pool, - test_utils::{get_user, server_with_pool}, + templates::{CreateUser, CreateUserSuccess}, + test_utils::{get_test_user, insert_user, massage, server_with_pool, FORM_CONTENT_TYPE}, + User, }; const GOOD_FORM: &str = "username=test_user&displayname=Test+User&password=aaaa&pw_verify=aaaa"; #[tokio::test] - async fn post_create_user_success() { - todo!() - } + async fn post_create_user() { + let pool = get_pool().await; + let server = server_with_pool(&pool).await; + let body = massage(GOOD_FORM); - mod failure { - // various ways to fuck up signup - const MISMATCH_PW_FORM: &str = - "username=test_user&displayname=Test+User&password=aaaa&pw_verify=bbbb"; - const SHORT_PW_FORM: &str = - "username=test_user&displayname=Test+User&password=a&pw_verify=a"; - const LONG_USERNAME_FORM: &str = - "username=test_user12345678901234567890&displayname=Test+User& - password=aaaa&pw_verify=aaaa"; + let _resp = server + .post("/signup") + .expect_failure() // 303 is "failure" + .bytes(body) + .content_type(FORM_CONTENT_TYPE) + .await; - #[tokio::test] - async fn mismatch_pw() { - todo!() - } - - #[tokio::test] - async fn too_short_pw() { - todo!() - } - - #[tokio::test] - async fn too_long_username() { - todo!() - } + // get the new user from the db + let user = User::try_get("test_user", &pool).await; + assert!(user.is_ok()); } #[tokio::test] async fn get_create_user() { - todo!() + let pool = get_pool().await; + let server = server_with_pool(&pool).await; + + let resp = server.get("/signup").await; + let body = std::str::from_utf8(resp.bytes()).unwrap(); + let expected = CreateUser::default().to_string(); + assert_eq!(&expected, body); } #[tokio::test] async fn handle_signup_success() { - todo!() + let pool = get_pool().await; + let server = server_with_pool(&pool).await; + + let user = get_test_user(); + insert_user(&user, &pool).await; + let id = user.id.as_simple().to_string(); + + let path = format!("/signup_success/{id}"); + + let resp = server.get(&path).expect_success().await; + let body = std::str::from_utf8(resp.bytes()).unwrap(); + let expected = CreateUserSuccess(user).to_string(); + assert_eq!(&expected, body); + } + + //-************************************************************************ + // honestly this is basically the whole suite here + //-************************************************************************ + mod failure { + use axum::http::StatusCode; + + use super::*; + use crate::signup::CreateUserError; + + // various ways to fuck up signup + const PASSWORD_MISMATCH_FORM: &str = + "username=test_user&displayname=Test+User&password=aaaa&pw_verify=bbbb"; + const PASSWORD_SHORT_FORM: &str = + "username=test_user&displayname=Test+User&password=a&pw_verify=a"; + const PASSWORD_LONG_FORM: &str = "username=test_user&displayname=Test+User&password=sphinx+of+black+qwartz+judge+my+vow+etc+etc+yadd+yadda&pw_verify=sphinx+of+black+qwartz+judge+my+vow+etc+etc+yadd+yadda"; + const USERNAME_SHORT_FORM: &str = + "username=&displayname=Test+User&password=aaaa&pw_verify=aaaa"; + const USERNAME_LONG_FORM: &str = + "username=test_user12345678901234567890&displayname=Test+User&password=aaaa&pw_verify=aaaa"; + const DISPLAYNAME_LONG_FORM: &str = "username=test_user&displayname=Since+time+immemorial%2C+display+names+have+been+subject+to+a+number+of+conventions%2C+restrictions%2C+usages%2C+and+even+incentives.+Have+we+finally+gone+too+far%3F+In+this+essay%2C+&password=aaaa&pw_verify=aaaa"; + + #[tokio::test] + async fn password_mismatch() { + let pool = get_pool().await; + let server = server_with_pool(&pool).await; + let body = massage(PASSWORD_MISMATCH_FORM); + + let resp = server + .post("/signup") + // failure to sign up is not failure to submit the request + .expect_success() + .bytes(body) + .content_type(FORM_CONTENT_TYPE) + .await; + + // no user in db + let user = User::try_get("test_user", &pool).await; + assert!(user.is_err()); + + let body = std::str::from_utf8(resp.bytes()).unwrap(); + let expected = + CreateUserError(crate::signup::CreateUserErrorKind::PasswordMismatch).to_string(); + assert_eq!(&expected, body); + } + + #[tokio::test] + async fn password_short() { + let pool = get_pool().await; + let server = server_with_pool(&pool).await; + let body = massage(PASSWORD_SHORT_FORM); + + let resp = server + .post("/signup") + // failure to sign up is not failure to submit the request + .expect_success() + .bytes(body) + .content_type(FORM_CONTENT_TYPE) + .await; + + // no user in db + let user = User::try_get("test_user", &pool).await; + assert!(user.is_err()); + + let body = std::str::from_utf8(resp.bytes()).unwrap(); + let expected = + CreateUserError(crate::signup::CreateUserErrorKind::BadPassword).to_string(); + assert_eq!(&expected, body); + } + + #[tokio::test] + async fn password_long() { + let pool = get_pool().await; + let server = server_with_pool(&pool).await; + let body = massage(PASSWORD_LONG_FORM); + + let resp = server + .post("/signup") + // failure to sign up is not failure to submit the request + .expect_success() + .bytes(body) + .content_type(FORM_CONTENT_TYPE) + .await; + + // no user in db + let user = User::try_get("test_user", &pool).await; + assert!(user.is_err()); + + let body = std::str::from_utf8(resp.bytes()).unwrap(); + let expected = + CreateUserError(crate::signup::CreateUserErrorKind::BadPassword).to_string(); + assert_eq!(&expected, body); + } + + #[tokio::test] + async fn username_short() { + let pool = get_pool().await; + let server = server_with_pool(&pool).await; + let body = massage(USERNAME_SHORT_FORM); + + let resp = server + .post("/signup") + // failure to sign up is not failure to submit the request + .expect_success() + .bytes(body) + .content_type(FORM_CONTENT_TYPE) + .await; + + // no user in db + let user = User::try_get("test_user", &pool).await; + assert!(user.is_err()); + + let body = std::str::from_utf8(resp.bytes()).unwrap(); + let expected = + CreateUserError(crate::signup::CreateUserErrorKind::BadUsername).to_string(); + assert_eq!(&expected, body); + } + + #[tokio::test] + async fn username_long() { + let pool = get_pool().await; + let server = server_with_pool(&pool).await; + let body = massage(USERNAME_LONG_FORM); + + let resp = server + .post("/signup") + // failure to sign up is not failure to submit the request + .expect_success() + .bytes(body) + .content_type(FORM_CONTENT_TYPE) + .await; + + // no user in db + let user = User::try_get("test_user", &pool).await; + assert!(user.is_err()); + + let body = std::str::from_utf8(resp.bytes()).unwrap(); + let expected = + CreateUserError(crate::signup::CreateUserErrorKind::BadUsername).to_string(); + assert_eq!(&expected, body); + } + + #[tokio::test] + async fn displayname_long() { + let pool = get_pool().await; + let server = server_with_pool(&pool).await; + let body = massage(DISPLAYNAME_LONG_FORM); + + let resp = server + .post("/signup") + // failure to sign up is not failure to submit the request + .expect_success() + .bytes(body) + .content_type(FORM_CONTENT_TYPE) + .await; + + // no user in db + let user = User::try_get("test_user", &pool).await; + assert!(user.is_err()); + + let body = std::str::from_utf8(resp.bytes()).unwrap(); + let expected = + CreateUserError(crate::signup::CreateUserErrorKind::BadDisplayname).to_string(); + assert_eq!(&expected, body); + } + + #[tokio::test] + async fn handle_signup_success() { + let pool = get_pool().await; + let server = server_with_pool(&pool).await; + + let path = format!("/signup_success/nope"); + + let resp = server.get(&path).expect_failure().await; + assert_eq!(resp.status_code(), StatusCode::SEE_OTHER); + } } } diff --git a/src/templates.rs b/src/templates.rs index 45ed268..9f8748d 100644 --- a/src/templates.rs +++ b/src/templates.rs @@ -13,6 +13,10 @@ pub struct CreateUser { pub pw_verify: String, } +#[derive(Debug, Clone, Template, Default, Deserialize, Serialize, PartialEq, Eq)] +#[template(path = "signup_success.html")] +pub struct CreateUserSuccess(pub User); + #[derive(Debug, Default, Template, Deserialize, Serialize, PartialEq, Eq)] #[template(path = "login_post.html")] pub struct LoginPost { diff --git a/src/test_utils.rs b/src/test_utils.rs index 3f6559e..20ae1b8 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -7,7 +7,7 @@ use crate::User; pub const FORM_CONTENT_TYPE: &str = "application/x-www-form-urlencoded"; -pub fn get_user() -> User { +pub fn get_test_user() -> User { User { username: "test_user".to_string(), // corresponding to a password of "a": @@ -22,7 +22,7 @@ pub async fn server() -> TestServer { let pool = crate::db::get_pool().await; let secret = [0u8; 64]; - let user = get_user(); + let user = get_test_user(); sqlx::query(crate::signup::CREATE_QUERY) .bind(user.id) .bind(&user.username) From 9292454dd3317161acb899a1edb730392d7944ec Mon Sep 17 00:00:00 2001 From: Joe Ardent Date: Sat, 3 Jun 2023 16:10:48 -0700 Subject: [PATCH 6/6] Add test for duplicate username. --- src/signup.rs | 59 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 16 deletions(-) diff --git a/src/signup.rs b/src/signup.rs index 02bb93f..dc8d479 100644 --- a/src/signup.rs +++ b/src/signup.rs @@ -233,6 +233,8 @@ pub(crate) async fn create_user( #[cfg(test)] mod test { + use axum::http::StatusCode; + use crate::{ db::get_pool, templates::{CreateUser, CreateUserSuccess}, @@ -248,13 +250,15 @@ mod test { let server = server_with_pool(&pool).await; let body = massage(GOOD_FORM); - let _resp = server + let resp = server .post("/signup") .expect_failure() // 303 is "failure" .bytes(body) .content_type(FORM_CONTENT_TYPE) .await; + assert_eq!(StatusCode::SEE_OTHER, resp.status_code()); + // get the new user from the db let user = User::try_get("test_user", &pool).await; assert!(user.is_ok()); @@ -292,10 +296,8 @@ mod test { // honestly this is basically the whole suite here //-************************************************************************ mod failure { - use axum::http::StatusCode; - use super::*; - use crate::signup::CreateUserError; + use crate::signup::{CreateUserError, CreateUserErrorKind}; // various ways to fuck up signup const PASSWORD_MISMATCH_FORM: &str = @@ -328,8 +330,7 @@ mod test { assert!(user.is_err()); let body = std::str::from_utf8(resp.bytes()).unwrap(); - let expected = - CreateUserError(crate::signup::CreateUserErrorKind::PasswordMismatch).to_string(); + let expected = CreateUserError(CreateUserErrorKind::PasswordMismatch).to_string(); assert_eq!(&expected, body); } @@ -352,8 +353,7 @@ mod test { assert!(user.is_err()); let body = std::str::from_utf8(resp.bytes()).unwrap(); - let expected = - CreateUserError(crate::signup::CreateUserErrorKind::BadPassword).to_string(); + let expected = CreateUserError(CreateUserErrorKind::BadPassword).to_string(); assert_eq!(&expected, body); } @@ -376,8 +376,7 @@ mod test { assert!(user.is_err()); let body = std::str::from_utf8(resp.bytes()).unwrap(); - let expected = - CreateUserError(crate::signup::CreateUserErrorKind::BadPassword).to_string(); + let expected = CreateUserError(CreateUserErrorKind::BadPassword).to_string(); assert_eq!(&expected, body); } @@ -400,8 +399,7 @@ mod test { assert!(user.is_err()); let body = std::str::from_utf8(resp.bytes()).unwrap(); - let expected = - CreateUserError(crate::signup::CreateUserErrorKind::BadUsername).to_string(); + let expected = CreateUserError(CreateUserErrorKind::BadUsername).to_string(); assert_eq!(&expected, body); } @@ -424,8 +422,38 @@ mod test { assert!(user.is_err()); let body = std::str::from_utf8(resp.bytes()).unwrap(); - let expected = - CreateUserError(crate::signup::CreateUserErrorKind::BadUsername).to_string(); + let expected = CreateUserError(CreateUserErrorKind::BadUsername).to_string(); + assert_eq!(&expected, body); + } + + #[tokio::test] + async fn username_duplicate() { + let pool = get_pool().await; + let server = server_with_pool(&pool).await; + let body = massage(GOOD_FORM); + + let _resp = server + .post("/signup") + .expect_failure() // 303 is "failure" + .bytes(body.clone()) + .content_type(FORM_CONTENT_TYPE) + .await; + + // get the new user from the db + let user = User::try_get("test_user", &pool).await; + assert!(user.is_ok()); + + // now try again + let resp = server + .post("/signup") + .expect_success() + .bytes(body) + .content_type(FORM_CONTENT_TYPE) + .await; + + assert_eq!(resp.status_code(), StatusCode::OK); + let expected = CreateUserError(CreateUserErrorKind::AlreadyExists).to_string(); + let body = std::str::from_utf8(resp.bytes()).unwrap(); assert_eq!(&expected, body); } @@ -448,8 +476,7 @@ mod test { assert!(user.is_err()); let body = std::str::from_utf8(resp.bytes()).unwrap(); - let expected = - CreateUserError(crate::signup::CreateUserErrorKind::BadDisplayname).to_string(); + let expected = CreateUserError(CreateUserErrorKind::BadDisplayname).to_string(); assert_eq!(&expected, body); }