-use crate::{
- schema::captcha_answer,
- source::captcha_answer::CaptchaAnswer,
- utils::{functions::lower, get_conn, naive_now, DbPool},
-};
-use diesel::{
- delete,
- dsl::exists,
- insert_into,
- result::Error,
- select,
- ExpressionMethods,
- QueryDsl,
-};
-use diesel_async::RunQueryDsl;
-
-impl CaptchaAnswer {
- pub async fn insert(pool: &DbPool, captcha: &CaptchaAnswer) -> Result<Self, Error> {
- let conn = &mut get_conn(pool).await?;
-
- insert_into(captcha_answer::table)
- .values(captcha)
- .get_result::<Self>(conn)
- .await
- }
-
- pub async fn check_captcha(pool: &DbPool, to_check: CaptchaAnswer) -> Result<bool, Error> {
- let conn = &mut get_conn(pool).await?;
-
- // delete any expired captchas
- delete(captcha_answer::table.filter(captcha_answer::expires.lt(&naive_now())))
- .execute(conn)
- .await?;
-
- // fetch requested captcha
- let captcha_exists = select(exists(
- captcha_answer::dsl::captcha_answer
- .filter((captcha_answer::dsl::uuid).eq(to_check.uuid.clone()))
- .filter(lower(captcha_answer::dsl::answer).eq(to_check.answer.to_lowercase().clone())),
- ))
- .get_result::<bool>(conn)
- .await?;
-
- // delete checked captcha
- delete(captcha_answer::table.filter(captcha_answer::uuid.eq(to_check.uuid.clone())))
- .execute(conn)
- .await?;
-
- Ok(captcha_exists)
- }
-}
-
-#[cfg(test)]
-mod tests {
- use crate::{
- source::captcha_answer::CaptchaAnswer,
- utils::{build_db_pool_for_tests, naive_now},
- };
- use chrono::Duration;
- use serial_test::serial;
-
- #[tokio::test]
- #[serial]
- async fn test_captcha_happy_path() {
- let pool = &build_db_pool_for_tests().await;
-
- let captcha_a_id = "a".to_string();
-
- let _ = CaptchaAnswer::insert(
- pool,
- &CaptchaAnswer {
- uuid: captcha_a_id.clone(),
- answer: "XYZ".to_string(),
- expires: naive_now() + Duration::minutes(10),
- },
- )
- .await;
-
- let result = CaptchaAnswer::check_captcha(
- pool,
- CaptchaAnswer {
- uuid: captcha_a_id.clone(),
- answer: "xyz".to_string(),
- expires: chrono::NaiveDateTime::MIN,
- },
- )
- .await;
-
- assert!(result.is_ok());
- assert!(result.unwrap());
- }
-
- #[tokio::test]
- #[serial]
- async fn test_captcha_repeat_answer_fails() {
- let pool = &build_db_pool_for_tests().await;
-
- let captcha_a_id = "a".to_string();
-
- let _ = CaptchaAnswer::insert(
- pool,
- &CaptchaAnswer {
- uuid: captcha_a_id.clone(),
- answer: "XYZ".to_string(),
- expires: naive_now() + Duration::minutes(10),
- },
- )
- .await;
-
- let result = CaptchaAnswer::check_captcha(
- pool,
- CaptchaAnswer {
- uuid: captcha_a_id.clone(),
- answer: "xyz".to_string(),
- expires: chrono::NaiveDateTime::MIN,
- },
- )
- .await;
-
- let result_repeat = CaptchaAnswer::check_captcha(
- pool,
- CaptchaAnswer {
- uuid: captcha_a_id.clone(),
- answer: "xyz".to_string(),
- expires: chrono::NaiveDateTime::MIN,
- },
- )
- .await;
-
- assert!(result_repeat.is_ok());
- assert!(!result_repeat.unwrap());
- }
-
- #[tokio::test]
- #[serial]
- async fn test_captcha_expired_fails() {
- let pool = &build_db_pool_for_tests().await;
-
- let expired_id = "already_expired".to_string();
-
- let _ = CaptchaAnswer::insert(
- pool,
- &CaptchaAnswer {
- uuid: expired_id.clone(),
- answer: "xyz".to_string(),
- expires: naive_now() - Duration::seconds(1),
- },
- )
- .await;
-
- let expired_result = CaptchaAnswer::check_captcha(
- pool,
- CaptchaAnswer {
- uuid: expired_id.clone(),
- answer: "xyz".to_string(),
- expires: chrono::NaiveDateTime::MIN,
- },
- )
- .await;
-
- assert!(expired_result.is_ok());
- assert!(!expired_result.unwrap());
- }
-}