Skip to content

Commit

Permalink
Merge pull request #51 from torrust/testing/database-trait
Browse files Browse the repository at this point in the history
Add integration tests for `Database` trait
  • Loading branch information
mickvandijke authored Aug 10, 2022
2 parents a939443 + 9400754 commit d6bd2f8
Show file tree
Hide file tree
Showing 16 changed files with 325 additions and 27 deletions.
7 changes: 2 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ async-trait = "0.1.52"
async-std = "1.10.0"
futures = "0.3.5"

sqlx = { version = "0.5.7", features = [ "runtime-actix-rustls", "sqlite", "mysql", "migrate", "time" ] }
sqlx = { version = "0.5.7", features = [ "runtime-tokio-native-tls", "sqlite", "mysql", "migrate", "time" ] }

config = "0.11"
toml = "0.5"
Expand Down
6 changes: 3 additions & 3 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use config::{ConfigError, Config, File};
use std::path::Path;
use serde::{Serialize, Deserialize};
use tokio::sync::RwLock;
use crate::databases::database::DatabaseDrivers;
use crate::databases::database::DatabaseDriver;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Website {
Expand Down Expand Up @@ -50,7 +50,7 @@ pub struct Auth {

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Database {
pub db_driver: DatabaseDrivers,
pub db_driver: DatabaseDriver,
pub connect_url: String,
pub torrent_info_update_interval: u64,
}
Expand Down Expand Up @@ -111,7 +111,7 @@ impl Configuration {
secret_key: "MaxVerstappenWC2021".to_string()
},
database: Database {
db_driver: DatabaseDrivers::Sqlite3,
db_driver: DatabaseDriver::Sqlite3,
connect_url: "sqlite://data.db?mode=rwc".to_string(),
torrent_info_update_interval: 3600
},
Expand Down
18 changes: 12 additions & 6 deletions src/databases/database.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ use crate::models::torrent::TorrentListing;
use crate::models::tracker_key::TrackerKey;
use crate::models::user::{User, UserAuthentication, UserCompact, UserProfile};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum DatabaseDrivers {
#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
pub enum DatabaseDriver {
Sqlite3,
Mysql
}
Expand Down Expand Up @@ -54,7 +54,7 @@ pub enum DatabaseError {
TorrentTitleAlreadyExists,
}

pub async fn connect_database(db_driver: &DatabaseDrivers, db_path: &str) -> Box<dyn Database> {
pub async fn connect_database(db_driver: &DatabaseDriver, db_path: &str) -> Box<dyn Database> {
// match &db_path.chars().collect::<Vec<char>>() as &[char] {
// ['s', 'q', 'l', 'i', 't', 'e', ..] => {
// let db = SqliteDatabase::new(db_path).await;
Expand All @@ -70,11 +70,11 @@ pub async fn connect_database(db_driver: &DatabaseDrivers, db_path: &str) -> Box
// }

match db_driver {
DatabaseDrivers::Sqlite3 => {
DatabaseDriver::Sqlite3 => {
let db = SqliteDatabase::new(db_path).await;
Box::new(db)
}
DatabaseDrivers::Mysql => {
DatabaseDriver::Mysql => {
let db = MysqlDatabase::new(db_path).await;
Box::new(db)
}
Expand All @@ -83,6 +83,9 @@ pub async fn connect_database(db_driver: &DatabaseDrivers, db_path: &str) -> Box

#[async_trait]
pub trait Database: Sync + Send {
// return current database driver
fn get_database_driver(&self) -> DatabaseDriver;

// add new user and get the newly inserted user_id
async fn insert_user_and_get_id(&self, username: &str, email: &str, password: &str) -> Result<i64, DatabaseError>;

Expand Down Expand Up @@ -122,7 +125,7 @@ pub trait Database: Sync + Send {
async fn delete_user(&self, user_id: i64) -> Result<(), DatabaseError>;

// add new category
async fn add_category(&self, category_name: &str) -> Result<(), DatabaseError>;
async fn insert_category_and_get_id(&self, category_name: &str) -> Result<i64, DatabaseError>;

// get category by id
async fn get_category_from_id(&self, id: i64) -> Result<Category, DatabaseError>;
Expand Down Expand Up @@ -159,4 +162,7 @@ pub trait Database: Sync + Send {

// delete a torrent
async fn delete_torrent(&self, torrent_id: i64) -> Result<(), DatabaseError>;

// DELETES ALL DATABASE ROWS, ONLY CALL THIS IF YOU KNOW WHAT YOU'RE DOING!
async fn delete_all_database_rows(&self) -> Result<(), DatabaseError>;
}
65 changes: 61 additions & 4 deletions src/databases/mysql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ use crate::models::user::{User, UserAuthentication, UserCompact, UserProfile};
use crate::models::torrent::TorrentListing;
use crate::utils::time::current_time;
use crate::models::tracker_key::TrackerKey;
use crate::databases::database::{Category, Database, DatabaseError, Sorting, TorrentCompact};
use crate::handlers::torrent::TorrentCount;
use crate::databases::database::{Category, Database, DatabaseDriver, DatabaseError, Sorting, TorrentCompact};
use crate::models::response::{TorrentsResponse};

pub struct MysqlDatabase {
Expand All @@ -35,6 +34,10 @@ impl MysqlDatabase {

#[async_trait]
impl Database for MysqlDatabase {
fn get_database_driver(&self) -> DatabaseDriver {
DatabaseDriver::Mysql
}

async fn insert_user_and_get_id(&self, username: &str, email: &str, password_hash: &str) -> Result<i64, DatabaseError> {

// open pool connection
Expand Down Expand Up @@ -227,12 +230,12 @@ impl Database for MysqlDatabase {
})
}

async fn add_category(&self, category_name: &str) -> Result<(), DatabaseError> {
async fn insert_category_and_get_id(&self, category_name: &str) -> Result<i64, DatabaseError> {
query("INSERT INTO torrust_categories (name) VALUES (?)")
.bind(category_name)
.execute(&self.pool)
.await
.map(|_| ())
.map(|v| v.last_insert_id() as i64)
.map_err(|e| match e {
sqlx::Error::Database(err) => {
if err.message().contains("UNIQUE") {
Expand Down Expand Up @@ -456,4 +459,58 @@ impl Database for MysqlDatabase {
Err(DatabaseError::TorrentNotFound)
})
}

async fn delete_all_database_rows(&self) -> Result<(), DatabaseError> {
query("DELETE FROM torrust_categories;")
.execute(&self.pool)
.await
.map_err(|_| DatabaseError::Error)?;

query("DELETE FROM torrust_torrents;")
.execute(&self.pool)
.await
.map_err(|_| DatabaseError::Error)?;

query("DELETE FROM torrust_tracker_keys;")
.execute(&self.pool)
.await
.map_err(|_| DatabaseError::Error)?;

query("DELETE FROM torrust_users;")
.execute(&self.pool)
.await
.map_err(|_| DatabaseError::Error)?;

query("DELETE FROM torrust_user_authentication;")
.execute(&self.pool)
.await
.map_err(|_| DatabaseError::Error)?;

query("DELETE FROM torrust_user_bans;")
.execute(&self.pool)
.await
.map_err(|_| DatabaseError::Error)?;

query("DELETE FROM torrust_user_invitations;")
.execute(&self.pool)
.await
.map_err(|_| DatabaseError::Error)?;

query("DELETE FROM torrust_user_profiles;")
.execute(&self.pool)
.await
.map_err(|_| DatabaseError::Error)?;

query("DELETE FROM torrust_torrents;")
.execute(&self.pool)
.await
.map_err(|_| DatabaseError::Error)?;

query("DELETE FROM torrust_user_public_keys;")
.execute(&self.pool)
.await
.map_err(|_| DatabaseError::Error)?;

Ok(())
}
}
65 changes: 61 additions & 4 deletions src/databases/sqlite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ use chrono::{NaiveDateTime};
use crate::models::torrent::TorrentListing;
use crate::utils::time::current_time;
use crate::models::tracker_key::TrackerKey;
use crate::databases::database::{Category, Database, DatabaseError, Sorting, TorrentCompact};
use crate::handlers::torrent::TorrentCount;
use crate::databases::database::{Category, Database, DatabaseDriver, DatabaseError, Sorting, TorrentCompact};
use crate::models::response::{TorrentsResponse};
use crate::models::user::{User, UserAuthentication, UserCompact, UserProfile};

Expand Down Expand Up @@ -35,6 +34,10 @@ impl SqliteDatabase {

#[async_trait]
impl Database for SqliteDatabase {
fn get_database_driver(&self) -> DatabaseDriver {
DatabaseDriver::Sqlite3
}

async fn insert_user_and_get_id(&self, username: &str, email: &str, password_hash: &str) -> Result<i64, DatabaseError> {

// open pool connection
Expand Down Expand Up @@ -223,12 +226,12 @@ impl Database for SqliteDatabase {
})
}

async fn add_category(&self, category_name: &str) -> Result<(), DatabaseError> {
async fn insert_category_and_get_id(&self, category_name: &str) -> Result<i64, DatabaseError> {
query("INSERT INTO torrust_categories (name) VALUES (?)")
.bind(category_name)
.execute(&self.pool)
.await
.map(|_| ())
.map(|v| v.last_insert_rowid())
.map_err(|e| match e {
sqlx::Error::Database(err) => {
if err.message().contains("UNIQUE") {
Expand Down Expand Up @@ -452,4 +455,58 @@ impl Database for SqliteDatabase {
Err(DatabaseError::TorrentNotFound)
})
}

async fn delete_all_database_rows(&self) -> Result<(), DatabaseError> {
query("DELETE FROM torrust_categories;")
.execute(&self.pool)
.await
.map_err(|_| DatabaseError::Error)?;

query("DELETE FROM torrust_torrents;")
.execute(&self.pool)
.await
.map_err(|_| DatabaseError::Error)?;

query("DELETE FROM torrust_tracker_keys;")
.execute(&self.pool)
.await
.map_err(|_| DatabaseError::Error)?;

query("DELETE FROM torrust_users;")
.execute(&self.pool)
.await
.map_err(|_| DatabaseError::Error)?;

query("DELETE FROM torrust_user_authentication;")
.execute(&self.pool)
.await
.map_err(|_| DatabaseError::Error)?;

query("DELETE FROM torrust_user_bans;")
.execute(&self.pool)
.await
.map_err(|_| DatabaseError::Error)?;

query("DELETE FROM torrust_user_invitations;")
.execute(&self.pool)
.await
.map_err(|_| DatabaseError::Error)?;

query("DELETE FROM torrust_user_profiles;")
.execute(&self.pool)
.await
.map_err(|_| DatabaseError::Error)?;

query("DELETE FROM torrust_torrents;")
.execute(&self.pool)
.await
.map_err(|_| DatabaseError::Error)?;

query("DELETE FROM torrust_user_public_keys;")
.execute(&self.pool)
.await
.map_err(|_| DatabaseError::Error)?;

Ok(())
}
}
2 changes: 1 addition & 1 deletion src/handlers/category.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ pub async fn add_category(req: HttpRequest, payload: web::Json<Category>, app_da
// check if user is administrator
if !user.administrator { return Err(ServiceError::Unauthorized) }

let _ = app_data.database.add_category(&payload.name).await?;
let _ = app_data.database.insert_category_and_get_id(&payload.name).await?;

Ok(HttpResponse::Ok().json(OkResponse {
data: payload.name.clone()
Expand Down
3 changes: 1 addition & 2 deletions src/models/torrent.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
use serde::{Deserialize, Serialize};
use crate::models::torrent_file::Torrent;
use crate::handlers::torrent::CreateTorrent;
use sqlx::{FromRow};

#[allow(dead_code)]
#[derive(Debug, Serialize, Deserialize, FromRow)]
#[derive(Debug, PartialEq, Serialize, Deserialize, sqlx::FromRow)]
pub struct TorrentListing {
pub torrent_id: i64,
pub uploader: String,
Expand Down
2 changes: 1 addition & 1 deletion src/models/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ pub struct UserAuthentication {
pub password_hash: String,
}

#[derive(Debug, Serialize, Deserialize, Clone, sqlx::FromRow)]
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone, sqlx::FromRow)]
pub struct UserProfile {
pub user_id: i64,
pub username: String,
Expand Down
10 changes: 10 additions & 0 deletions tests/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
### Running Tests
Torrust requires Docker to run different database systems for testing. [install docker here](https://docs.docker.com/engine/).

Start the databases with `docker-compose` before running tests:

$ docker-compose up

Run all tests using:

$ cargo test
29 changes: 29 additions & 0 deletions tests/databases/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use std::future::Future;
use torrust_index_backend::databases::database::{connect_database, Database, DatabaseDriver};

mod mysql;
mod tests;
mod sqlite;

// used to run tests with a clean database
async fn run_test<'a, T, F>(db_fn: T, db: &'a Box<dyn Database>)
where
T: FnOnce(&'a Box<dyn Database>) -> F + 'a,
F: Future<Output = ()>
{
// cleanup database before testing
assert!(db.delete_all_database_rows().await.is_ok());

// run test using clean database
db_fn(db).await;
}

// runs all tests
pub async fn run_tests(db_driver: DatabaseDriver, db_path: &str) {
let db = connect_database(&db_driver, db_path).await;

run_test(tests::it_can_add_a_user, &db).await;
run_test(tests::it_can_add_a_torrent_category, &db).await;
run_test(tests::it_can_add_a_torrent, &db).await;
}

11 changes: 11 additions & 0 deletions tests/databases/mysql.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use torrust_index_backend::databases::database::{DatabaseDriver};
use crate::databases::{run_tests};

const DATABASE_URL: &str = "mysql://root:password@localhost:3306/torrust-index_test";

#[tokio::test]
async fn run_mysql_tests() {
run_tests(DatabaseDriver::Mysql, DATABASE_URL).await;
}


Loading

0 comments on commit d6bd2f8

Please sign in to comment.