Add tests for bad invite signups.
This commit is contained in:
parent
c2bad4bb94
commit
c63786a3fc
3 changed files with 135 additions and 24 deletions
|
@ -8,7 +8,7 @@ use sqlx::{
|
|||
|
||||
const MAX_CONNS: u32 = 200;
|
||||
const MIN_CONNS: u32 = 5;
|
||||
const TIMEOUT: u64 = 11;
|
||||
const TIMEOUT: u64 = 3;
|
||||
|
||||
pub fn get_db_pool() -> SqlitePool {
|
||||
let db_filename = {
|
||||
|
|
|
@ -196,8 +196,7 @@ pub(crate) async fn create_user(
|
|||
CreateUserErrorKind::UnknownDBError
|
||||
})?;
|
||||
|
||||
let invitation =
|
||||
Julid::from_str(invitation).map_err(|_| CreateUserErrorKind::BadInvitation)?;
|
||||
let invitation = Julid::from_str(invitation).map_err(|_| CreateUserErrorKind::BadInvitation)?;
|
||||
|
||||
let invited_by = validate_invitation(invitation, &mut tx).await?;
|
||||
|
||||
|
@ -370,23 +369,110 @@ mod test {
|
|||
// honestly this is basically the whole suite here
|
||||
//-************************************************************************
|
||||
mod failure {
|
||||
use super::*;
|
||||
use crate::signup::handlers::{CreateUserError, CreateUserErrorKind};
|
||||
use std::time::Duration;
|
||||
|
||||
// various ways to fuck up signup
|
||||
const PASSWORD_MISMATCH_FORM: &str =
|
||||
"username=bad_user&displayname=Bad+User&password=aaaa&pw_verify=bbbb&invitation=0000000000000000000000001A";
|
||||
const PASSWORD_SHORT_FORM: &str =
|
||||
"username=bad_user&displayname=Bad+User&password=a&pw_verify=a&invitation=0000000000000000000000001A";
|
||||
const PASSWORD_LONG_FORM: &str = "username=bad_user&displayname=Bad+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&invitation=0000000000000000000000001A";
|
||||
const USERNAME_SHORT_FORM: &str =
|
||||
"username=&displayname=Bad+User&password=aaaa&pw_verify=aaaa&invitation=0000000000000000000000001A";
|
||||
const USERNAME_LONG_FORM: &str =
|
||||
"username=bad_user12345678901234567890&displayname=Bad+User&password=aaaa&pw_verify=aaaa&invitation=0000000000000000000000001A";
|
||||
const DISPLAYNAME_LONG_FORM: &str = "username=bad_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&invitation=0000000000000000000000001A";
|
||||
use axum::response::IntoResponse;
|
||||
|
||||
use super::*;
|
||||
use crate::{
|
||||
signup::handlers::{CreateUserError, CreateUserErrorKind},
|
||||
Invitation,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn used_up_invite() {
|
||||
let lucky1 = "username=lucky1&password=aaaa&pw_verify=aaaa&invitation=0000000000000000000000001A";
|
||||
let lucky2 = "username=lucky2&password=aaaa&pw_verify=aaaa&invitation=0000000000000000000000001A";
|
||||
let unlucky = "username=lucky3&password=aaaa&pw_verify=aaaa&invitation=0000000000000000000000001A";
|
||||
let pool = get_db_pool();
|
||||
let rt = Runtime::new().unwrap();
|
||||
rt.block_on(async {
|
||||
let server = server_with_pool(&pool).await;
|
||||
let body = massage(lucky1);
|
||||
|
||||
let _ = server
|
||||
.post("/signup")
|
||||
// 303 is "failure", but that's a successful signup
|
||||
.expect_failure()
|
||||
.bytes(body)
|
||||
.content_type(FORM_CONTENT_TYPE)
|
||||
.await;
|
||||
let user = User::try_get("lucky1", &pool).await;
|
||||
assert!(user.is_ok() && user.unwrap().is_some());
|
||||
|
||||
let body = massage(lucky2);
|
||||
let _ = server
|
||||
.post("/signup")
|
||||
.expect_failure()
|
||||
.bytes(body)
|
||||
.content_type(FORM_CONTENT_TYPE)
|
||||
.await;
|
||||
let user = User::try_get("lucky2", &pool).await;
|
||||
assert!(user.is_ok() && user.unwrap().is_some());
|
||||
|
||||
let body = massage(unlucky);
|
||||
let resp = server
|
||||
.post("/signup")
|
||||
// failure to sign up is not a failed request
|
||||
.expect_success()
|
||||
.bytes(body)
|
||||
.content_type(FORM_CONTENT_TYPE)
|
||||
.await;
|
||||
let user = User::try_get("unlucky", &pool).await;
|
||||
assert!(user.is_ok() && user.unwrap().is_none());
|
||||
|
||||
let body = resp.as_bytes();
|
||||
let expected: CreateUserError = CreateUserErrorKind::BadInvitation.into();
|
||||
let expected = expected.into_response().into_body();
|
||||
let expected = axum::body::to_bytes(expected, usize::MAX).await.unwrap();
|
||||
|
||||
assert_eq!(&expected, body);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expired_invite() {
|
||||
let pool = get_db_pool();
|
||||
let rt = Runtime::new().unwrap();
|
||||
rt.block_on(async {
|
||||
// this function adds a user with the omega id, so the invite can be added
|
||||
let server = server_with_pool(&pool).await;
|
||||
|
||||
let invite =
|
||||
Invitation::new(Julid::omega()).with_expires_in(Duration::from_millis(1));
|
||||
let invite = invite.commit(&pool).await.unwrap();
|
||||
|
||||
std::thread::sleep(Duration::from_millis(100));
|
||||
|
||||
let tooslow =
|
||||
format!("username=tooslow&password=aaaa&pw_verify=aaaa&invitation={invite}");
|
||||
let body = massage(&tooslow);
|
||||
|
||||
let resp = server
|
||||
.post("/signup")
|
||||
// failure to sign up is not a failed request
|
||||
.expect_success()
|
||||
.bytes(body)
|
||||
.content_type(FORM_CONTENT_TYPE)
|
||||
.await;
|
||||
let user = User::try_get("unlucky", &pool).await;
|
||||
assert!(user.is_ok() && user.unwrap().is_none());
|
||||
|
||||
let body = String::from_utf8(resp.as_bytes().to_vec()).unwrap();
|
||||
let expected: CreateUserError = CreateUserErrorKind::BadInvitation.into();
|
||||
let expected = expected.into_response().into_body();
|
||||
let bytes = axum::body::to_bytes(expected, usize::MAX).await.unwrap();
|
||||
let expected = String::from_utf8(bytes.to_vec()).unwrap();
|
||||
|
||||
assert_eq!(&expected, &body);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn password_mismatch() {
|
||||
const PASSWORD_MISMATCH_FORM: &str =
|
||||
"username=bad_user&displayname=Bad+User&password=aaaa&pw_verify=bbbb&invitation=0000000000000000000000001A";
|
||||
|
||||
let pool = get_db_pool();
|
||||
let rt = Runtime::new().unwrap();
|
||||
rt.block_on(async {
|
||||
|
@ -396,7 +482,7 @@ mod test {
|
|||
let resp = server
|
||||
.post("/signup")
|
||||
// failure to sign up is not failure to submit the request
|
||||
//.expect_success()
|
||||
.expect_success()
|
||||
.bytes(body)
|
||||
.content_type(FORM_CONTENT_TYPE)
|
||||
.await;
|
||||
|
@ -413,6 +499,9 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn password_short() {
|
||||
const PASSWORD_SHORT_FORM: &str =
|
||||
"username=bad_user&displayname=Bad+User&password=a&pw_verify=a&invitation=0000000000000000000000001A";
|
||||
|
||||
let pool = get_db_pool();
|
||||
let rt = Runtime::new().unwrap();
|
||||
rt.block_on(async {
|
||||
|
@ -439,6 +528,7 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn password_long() {
|
||||
const PASSWORD_LONG_FORM: &str = "username=bad_user&displayname=Bad+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&invitation=0000000000000000000000001A";
|
||||
let pool = get_db_pool();
|
||||
let rt = Runtime::new().unwrap();
|
||||
rt.block_on(async {
|
||||
|
@ -473,10 +563,12 @@ mod test {
|
|||
let pool = get_db_pool();
|
||||
let rt = Runtime::new().unwrap();
|
||||
|
||||
let invitation: Julid = INVITE_ID_INT.into();
|
||||
|
||||
rt.block_on(async {
|
||||
let server = server_with_pool(&pool).await;
|
||||
let form =
|
||||
format!("username=bad_user&displayname=Test+User&password={pw}&pw_verify={pw}&invitation=0");
|
||||
format!("username=bad_user&displayname=Test+User&password={pw}&pw_verify={pw}&invitation={invitation}");
|
||||
let body = massage(&form);
|
||||
|
||||
let resp = server
|
||||
|
@ -499,6 +591,9 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn username_short() {
|
||||
const USERNAME_SHORT_FORM: &str =
|
||||
"username=&displayname=Bad+User&password=aaaa&pw_verify=aaaa&invitation=0000000000000000000000001A";
|
||||
|
||||
let pool = get_db_pool();
|
||||
let rt = Runtime::new().unwrap();
|
||||
rt.block_on(async {
|
||||
|
@ -525,6 +620,9 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn username_long() {
|
||||
const USERNAME_LONG_FORM: &str =
|
||||
"username=bad_user12345678901234567890&displayname=Bad+User&password=aaaa&pw_verify=aaaa&invitation=0000000000000000000000001A";
|
||||
|
||||
let pool = get_db_pool();
|
||||
let rt = Runtime::new().unwrap();
|
||||
rt.block_on(async {
|
||||
|
@ -588,6 +686,7 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn displayname_long() {
|
||||
const DISPLAYNAME_LONG_FORM: &str = "username=bad_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&invitation=0000000000000000000000001A";
|
||||
let pool = get_db_pool();
|
||||
let rt = Runtime::new().unwrap();
|
||||
rt.block_on(async {
|
||||
|
|
|
@ -15,14 +15,15 @@ pub struct CreateInviteError(#[from] CreateInviteErrorKind);
|
|||
#[non_exhaustive]
|
||||
pub enum CreateInviteErrorKind {
|
||||
DBError,
|
||||
TooManyUses,
|
||||
NoOwner,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, PartialEq, Eq, Serialize, Deserialize, sqlx::FromRow)]
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, sqlx::FromRow)]
|
||||
pub struct Invitation {
|
||||
pub id: Julid,
|
||||
pub owner: Julid,
|
||||
pub expires_at: Option<i64>,
|
||||
id: Julid,
|
||||
owner: Julid,
|
||||
expires_at: Option<i64>,
|
||||
remaining: i16,
|
||||
}
|
||||
|
||||
|
@ -38,7 +39,18 @@ impl Invitation {
|
|||
.await
|
||||
.map_err(|e| {
|
||||
tracing::debug!("Got error creating invite: {e}");
|
||||
CreateInviteErrorKind::DBError
|
||||
match e {
|
||||
sqlx::Error::Database(dbe) => {
|
||||
let exit = dbe.code().unwrap_or_default().parse().unwrap_or(0);
|
||||
// https://www.sqlite.org/rescode.html#constraint_foreignkey
|
||||
if exit == 787u32 {
|
||||
CreateInviteErrorKind::NoOwner
|
||||
} else {
|
||||
CreateInviteErrorKind::DBError
|
||||
}
|
||||
}
|
||||
_ => CreateInviteErrorKind::Unknown,
|
||||
}
|
||||
})?
|
||||
.ok_or(CreateInviteErrorKind::DBError.into())
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue