fix and chore: refactor tests to minimize initializing DB connections

SUMMARY
    The test suite was spinning up way too many database connections that what's
    strictly needed and so the test suite was failing with[0]:
	code: "53300", message: "sorry, too many clients already"

EXPERIMENTS
    Tried sharing database connection pool across all tests with
    async_once[0] but faced:
	- IO errors
	    The connections were probably getting dropped in between tests
	- actix Actor errors
	    The actor was probably not getting initialized before a
	    a reference to the async_once initialized app
	    context(crate::data::Data) is retrieved and used

FIX
    crate::tests was spinning up an App context
    instance(crate::data::Data) for most utility functions, which was
    unnecessarily excessive.

    Each test now creates an instance of the application context at the
    beginning and shared a reference with all test utility functions. So
    number of database connections/app context instance = number of unit
    tests.

[0]: permanently fixes #22
[1]: https://docs.rs/async_once/latest/async_once/
This commit is contained in:
realaravinth
2022-05-14 12:55:56 +05:30
parent 176df3c7a7
commit a971d4209d
24 changed files with 243 additions and 274 deletions

View File

@@ -96,28 +96,27 @@ pub fn services(cfg: &mut actix_web::web::ServiceConfig) {
}
#[cfg(test)]
mod tests {
pub mod tests {
use super::*;
use actix_web::http::StatusCode;
use actix_web::test;
use crate::api::v1::ROUTES;
use crate::data::Data;
use crate::tests::*;
#[actix_rt::test]
async fn update_password_works() {
pub async fn update_password_works() {
const NAME: &str = "updatepassuser";
const PASSWORD: &str = "longpassword2";
const EMAIL: &str = "updatepassuser@a.com";
{
let data = Data::new().await;
delete_user(NAME, &data).await;
}
let data = crate::data::Data::new().await;
let data = &data;
let (data, _, signin_resp) = register_and_signin(NAME, EMAIL, PASSWORD).await;
delete_user(data, NAME).await;
let (_, signin_resp) = register_and_signin(data, NAME, EMAIL, PASSWORD).await;
let cookies = get_cookie!(signin_resp);
let app = get_app!(data).await;
@@ -129,7 +128,7 @@ mod tests {
confirm_new_password: PASSWORD.into(),
};
let res = update_password_runner(NAME, update_password.into(), &data).await;
let res = update_password_runner(NAME, update_password.into(), data).await;
assert!(res.is_err());
assert_eq!(res, Err(ServiceError::PasswordsDontMatch));
@@ -139,7 +138,7 @@ mod tests {
confirm_new_password: new_password.into(),
};
assert!(update_password_runner(NAME, update_password.into(), &data)
assert!(update_password_runner(NAME, update_password.into(), data)
.await
.is_ok());
@@ -150,6 +149,7 @@ mod tests {
};
bad_post_req_test(
data,
NAME,
new_password,
ROUTES.account.update_password,
@@ -165,6 +165,7 @@ mod tests {
};
bad_post_req_test(
data,
NAME,
new_password,
ROUTES.account.update_password,

View File

@@ -1,19 +1,19 @@
/*
* Copyright (C) 2022 Aravinth Manivannan <realaravinth@batsense.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
* Copyright (C) 2022 Aravinth Manivannan <realaravinth@batsense.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use actix_web::http::StatusCode;
use actix_web::test;
@@ -23,24 +23,21 @@ use super::username::Username;
use super::*;
use crate::api::v1::auth::runners::Password;
use crate::api::v1::ROUTES;
use crate::data::Data;
use crate::*;
use crate::errors::*;
use crate::tests::*;
#[actix_rt::test]
async fn uname_email_exists_works() {
pub async fn uname_email_exists_works() {
const NAME: &str = "testuserexists";
const PASSWORD: &str = "longpassword2";
const EMAIL: &str = "testuserexists@a.com2";
let data = crate::data::Data::new().await;
let data = &data;
delete_user(data, NAME).await;
{
let data = Data::new().await;
delete_user(NAME, &data).await;
}
let (data, _, signin_resp) = register_and_signin(NAME, EMAIL, PASSWORD).await;
let (_, signin_resp) = register_and_signin(data, NAME, EMAIL, PASSWORD).await;
let cookies = get_cookie!(signin_resp);
let app = get_app!(data).await;
@@ -118,21 +115,20 @@ async fn uname_email_exists_works() {
}
#[actix_rt::test]
async fn email_udpate_password_validation_del_userworks() {
pub async fn email_udpate_password_validation_del_userworks() {
const NAME: &str = "testuser2";
const PASSWORD: &str = "longpassword2";
const EMAIL: &str = "testuser1@a.com2";
const NAME2: &str = "eupdauser";
const EMAIL2: &str = "eupdauser@a.com";
{
let data = Data::new().await;
delete_user(NAME, &data).await;
delete_user(NAME2, &data).await;
}
let data = crate::data::Data::new().await;
let data = &data;
delete_user(data, NAME).await;
delete_user(data, NAME2).await;
let _ = register_and_signin(NAME2, EMAIL2, PASSWORD).await;
let (data, _creds, signin_resp) = register_and_signin(NAME, EMAIL, PASSWORD).await;
let _ = register_and_signin(data, NAME2, EMAIL2, PASSWORD).await;
let (_creds, signin_resp) = register_and_signin(data, NAME, EMAIL, PASSWORD).await;
let cookies = get_cookie!(signin_resp);
let app = get_app!(data).await;
@@ -153,6 +149,7 @@ async fn email_udpate_password_validation_del_userworks() {
// check duplicate email while duplicate email
email_payload.email = EMAIL2.into();
bad_post_req_test(
data,
NAME,
PASSWORD,
ROUTES.account.update_email,
@@ -166,6 +163,7 @@ async fn email_udpate_password_validation_del_userworks() {
password: NAME.into(),
};
bad_post_req_test(
data,
NAME,
PASSWORD,
ROUTES.account.delete,
@@ -200,7 +198,7 @@ async fn email_udpate_password_validation_del_userworks() {
}
#[actix_rt::test]
async fn username_update_works() {
pub async fn username_update_works() {
const NAME: &str = "testuserupda";
const EMAIL: &str = "testuserupda@sss.com";
const EMAIL2: &str = "testuserupda2@sss.com";
@@ -208,18 +206,17 @@ async fn username_update_works() {
const NAME2: &str = "terstusrtds";
const NAME_CHANGE: &str = "terstusrtdsxx";
{
let data = Data::new().await;
let data = crate::data::Data::new().await;
let data = &data;
futures::join!(
delete_user(NAME, &data),
delete_user(NAME2, &data),
delete_user(NAME_CHANGE, &data)
);
}
futures::join!(
delete_user(data, NAME),
delete_user(data, NAME2),
delete_user(data, NAME_CHANGE),
);
let _ = register_and_signin(NAME2, EMAIL2, PASSWORD).await;
let (data, _creds, signin_resp) = register_and_signin(NAME, EMAIL, PASSWORD).await;
let _ = register_and_signin(data, NAME2, EMAIL2, PASSWORD).await;
let (_creds, signin_resp) = register_and_signin(data, NAME, EMAIL, PASSWORD).await;
let cookies = get_cookie!(signin_resp);
let app = get_app!(data).await;
@@ -239,6 +236,7 @@ async fn username_update_works() {
// check duplicate username with duplicate username
username_udpate.username = NAME2.into();
bad_post_req_test(
data,
NAME_CHANGE,
PASSWORD,
ROUTES.account.update_username,