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 MAX_CONNS: u32 = 200;
|
||||||
const MIN_CONNS: u32 = 5;
|
const MIN_CONNS: u32 = 5;
|
||||||
const TIMEOUT: u64 = 11;
|
const TIMEOUT: u64 = 3;
|
||||||
|
|
||||||
pub fn get_db_pool() -> SqlitePool {
|
pub fn get_db_pool() -> SqlitePool {
|
||||||
let db_filename = {
|
let db_filename = {
|
||||||
|
|
|
@ -196,8 +196,7 @@ pub(crate) async fn create_user(
|
||||||
CreateUserErrorKind::UnknownDBError
|
CreateUserErrorKind::UnknownDBError
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let invitation =
|
let invitation = Julid::from_str(invitation).map_err(|_| CreateUserErrorKind::BadInvitation)?;
|
||||||
Julid::from_str(invitation).map_err(|_| CreateUserErrorKind::BadInvitation)?;
|
|
||||||
|
|
||||||
let invited_by = validate_invitation(invitation, &mut tx).await?;
|
let invited_by = validate_invitation(invitation, &mut tx).await?;
|
||||||
|
|
||||||
|
@ -370,23 +369,110 @@ mod test {
|
||||||
// honestly this is basically the whole suite here
|
// honestly this is basically the whole suite here
|
||||||
//-************************************************************************
|
//-************************************************************************
|
||||||
mod failure {
|
mod failure {
|
||||||
use super::*;
|
use std::time::Duration;
|
||||||
use crate::signup::handlers::{CreateUserError, CreateUserErrorKind};
|
|
||||||
|
|
||||||
// various ways to fuck up signup
|
use axum::response::IntoResponse;
|
||||||
const PASSWORD_MISMATCH_FORM: &str =
|
|
||||||
"username=bad_user&displayname=Bad+User&password=aaaa&pw_verify=bbbb&invitation=0000000000000000000000001A";
|
use super::*;
|
||||||
const PASSWORD_SHORT_FORM: &str =
|
use crate::{
|
||||||
"username=bad_user&displayname=Bad+User&password=a&pw_verify=a&invitation=0000000000000000000000001A";
|
signup::handlers::{CreateUserError, CreateUserErrorKind},
|
||||||
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";
|
Invitation,
|
||||||
const USERNAME_SHORT_FORM: &str =
|
};
|
||||||
"username=&displayname=Bad+User&password=aaaa&pw_verify=aaaa&invitation=0000000000000000000000001A";
|
|
||||||
const USERNAME_LONG_FORM: &str =
|
#[test]
|
||||||
"username=bad_user12345678901234567890&displayname=Bad+User&password=aaaa&pw_verify=aaaa&invitation=0000000000000000000000001A";
|
fn used_up_invite() {
|
||||||
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 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]
|
#[test]
|
||||||
fn password_mismatch() {
|
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 pool = get_db_pool();
|
||||||
let rt = Runtime::new().unwrap();
|
let rt = Runtime::new().unwrap();
|
||||||
rt.block_on(async {
|
rt.block_on(async {
|
||||||
|
@ -396,7 +482,7 @@ mod test {
|
||||||
let resp = server
|
let resp = server
|
||||||
.post("/signup")
|
.post("/signup")
|
||||||
// failure to sign up is not failure to submit the request
|
// failure to sign up is not failure to submit the request
|
||||||
//.expect_success()
|
.expect_success()
|
||||||
.bytes(body)
|
.bytes(body)
|
||||||
.content_type(FORM_CONTENT_TYPE)
|
.content_type(FORM_CONTENT_TYPE)
|
||||||
.await;
|
.await;
|
||||||
|
@ -413,6 +499,9 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn password_short() {
|
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 pool = get_db_pool();
|
||||||
let rt = Runtime::new().unwrap();
|
let rt = Runtime::new().unwrap();
|
||||||
rt.block_on(async {
|
rt.block_on(async {
|
||||||
|
@ -439,6 +528,7 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn password_long() {
|
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 pool = get_db_pool();
|
||||||
let rt = Runtime::new().unwrap();
|
let rt = Runtime::new().unwrap();
|
||||||
rt.block_on(async {
|
rt.block_on(async {
|
||||||
|
@ -473,10 +563,12 @@ mod test {
|
||||||
let pool = get_db_pool();
|
let pool = get_db_pool();
|
||||||
let rt = Runtime::new().unwrap();
|
let rt = Runtime::new().unwrap();
|
||||||
|
|
||||||
|
let invitation: Julid = INVITE_ID_INT.into();
|
||||||
|
|
||||||
rt.block_on(async {
|
rt.block_on(async {
|
||||||
let server = server_with_pool(&pool).await;
|
let server = server_with_pool(&pool).await;
|
||||||
let form =
|
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 body = massage(&form);
|
||||||
|
|
||||||
let resp = server
|
let resp = server
|
||||||
|
@ -499,6 +591,9 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn username_short() {
|
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 pool = get_db_pool();
|
||||||
let rt = Runtime::new().unwrap();
|
let rt = Runtime::new().unwrap();
|
||||||
rt.block_on(async {
|
rt.block_on(async {
|
||||||
|
@ -525,6 +620,9 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn username_long() {
|
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 pool = get_db_pool();
|
||||||
let rt = Runtime::new().unwrap();
|
let rt = Runtime::new().unwrap();
|
||||||
rt.block_on(async {
|
rt.block_on(async {
|
||||||
|
@ -588,6 +686,7 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn displayname_long() {
|
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 pool = get_db_pool();
|
||||||
let rt = Runtime::new().unwrap();
|
let rt = Runtime::new().unwrap();
|
||||||
rt.block_on(async {
|
rt.block_on(async {
|
||||||
|
|
|
@ -15,14 +15,15 @@ pub struct CreateInviteError(#[from] CreateInviteErrorKind);
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub enum CreateInviteErrorKind {
|
pub enum CreateInviteErrorKind {
|
||||||
DBError,
|
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 struct Invitation {
|
||||||
pub id: Julid,
|
id: Julid,
|
||||||
pub owner: Julid,
|
owner: Julid,
|
||||||
pub expires_at: Option<i64>,
|
expires_at: Option<i64>,
|
||||||
remaining: i16,
|
remaining: i16,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +39,18 @@ impl Invitation {
|
||||||
.await
|
.await
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
tracing::debug!("Got error creating invite: {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())
|
.ok_or(CreateInviteErrorKind::DBError.into())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue