Skip to content

Commit

Permalink
Added integration tests for the crud functions on all three DB types
Browse files Browse the repository at this point in the history
  • Loading branch information
meuter committed Apr 7, 2024
1 parent 856fd56 commit d7e7475
Show file tree
Hide file tree
Showing 9 changed files with 268 additions and 3 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,6 @@ jobs:
run: cargo build --workspace --no-default-features -F sqlite
- name: Build only with MySql
run: cargo build --workspace --no-default-features -F mysql
- name: Build only with Axum
run: cargo build --workspace --no-default-features -F axum

2 changes: 1 addition & 1 deletion .github/workflows/clippy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ env:
RUSTFLAGS: "-Dwarnings" # Make sure CI fails on all warnings, including Clippy lints

jobs:
clippy_check:
clippy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ env:
RUSTFLAGS: "-Dwarnings" # Make sure CI fails on all warnings, including Clippy lints

jobs:
clippy_check:
documentation:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
Expand Down
12 changes: 11 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ env:
CARGO_TERM_COLOR: always

jobs:
build:
unit_test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
Expand All @@ -15,3 +15,13 @@ jobs:
- uses: actions/checkout@v2
- name: Test with default feature
run: cargo test --workspace
integration_tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Start Postgres and MySql sandbox
run: docker compose up -d
- name: Run all integration tests
run: cargo test -F integration_tests
- name: Stop Postgres sandbox
run: docker compose down
55 changes: 55 additions & 0 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ axum = ["dep:axum", "serde"]
postgres = ["sqlx/postgres"]
sqlite = ["sqlx/sqlite"]
mysql = ["sqlx/mysql"]
integration_tests = []

[dev-dependencies]
sqlx = { version = "0.7.4", features = [
Expand All @@ -46,3 +47,4 @@ serde = { version = "1.0.197", features = ["derive"] }
serde_json = "1.0.114"
iso_currency = { version = "0.4.4", features = ["serde", "with-serde"] }
miniorm = { path = ".", features = ["full"] }
serial_test = "3.0.0"
3 changes: 3 additions & 0 deletions tests/common/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mod todo;

pub use todo::Todo;
35 changes: 35 additions & 0 deletions tests/common/todo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use miniorm::Entity;
use sqlx::FromRow;

#[derive(Debug, Clone, Eq, PartialEq, FromRow, Entity, Hash)]
#[cfg_attr(feature = "axum", derive(serde::Serialize, serde::Deserialize))]
pub struct Todo {
#[column(TEXT NOT NULL)]
description: String,

#[column(BOOLEAN NOT NULL DEFAULT false)]
done: bool,
}

impl Todo {
pub fn new(description: impl AsRef<str>) -> Self {
let description = description.as_ref().to_string();
let done = false;
Todo { description, done }
}

#[allow(unused)]
pub fn is_done(&self) -> bool {
self.done
}

#[allow(unused)]
pub fn description(&self) -> &str {
&self.description
}

#[allow(unused)]
pub fn mark_as_done(&mut self) {
self.done = true;
}
}
158 changes: 158 additions & 0 deletions tests/test_crud.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
mod common;

#[macro_export]
macro_rules! test_todo_crud {
($new_store: expr) => {
#[cfg_attr(not(feature = "integration_tests"), ignore)]
#[serial]
#[tokio::test]
async fn create() {
#[allow(unused_mut)]
let mut store = $new_store;
let todo = store.create(Todo::new("checkout miniorm")).await.unwrap();
assert_eq!(todo.id(), 1);
assert_eq!(todo.description(), "checkout miniorm");
assert!(!todo.is_done());
}

#[cfg_attr(not(feature = "integration_tests"), ignore)]
#[serial]
#[tokio::test]
async fn read() {
#[allow(unused_mut)]
let mut store = $new_store;
let todo = store.create(Todo::new("checkout miniorm")).await.unwrap();
assert_eq!(todo, store.read(todo.id()).await.unwrap());
}

#[cfg_attr(not(feature = "integration_tests"), ignore)]
#[serial]
#[tokio::test]
async fn list() {
#[allow(unused_mut)]
let mut store = $new_store;
let todo1 = store.create(Todo::new("todo1")).await.unwrap();
let todo2 = store.create(Todo::new("todo2")).await.unwrap();
let todo3 = store.create(Todo::new("todo3")).await.unwrap();

let all_todos = store.list().await.unwrap();
assert_eq!(all_todos, [todo1, todo2, todo3]);
}

#[cfg_attr(not(feature = "integration_tests"), ignore)]
#[serial]
#[tokio::test]
async fn update() {
#[allow(unused_mut)]
let mut store = $new_store;
let mut todo = store.create(Todo::new("checkout miniorm")).await.unwrap();
let id = todo.id();

assert!(!store.read(id).await.unwrap().is_done());

todo.mark_as_done();
assert!(!store.read(id).await.unwrap().is_done());

store.update(todo).await.unwrap();
assert!(store.read(id).await.unwrap().is_done());
}

#[cfg_attr(not(feature = "integration_tests"), ignore)]
#[serial]
#[tokio::test]
async fn delete() {
#[allow(unused_mut)]
let mut store = $new_store;
let todo = store.create(Todo::new("checkout miniorm")).await.unwrap();

store.delete(todo.id()).await.unwrap();

assert!(matches!(
store.delete(todo.id()).await,
Err(sqlx::Error::RowNotFound)
));

assert!(matches!(
store.read(todo.id()).await,
Err(sqlx::Error::RowNotFound)
));
}

#[cfg_attr(not(feature = "integration_tests"), ignore)]
#[serial]
#[tokio::test]
async fn delete_all() {
#[allow(unused_mut)]
let mut store = $new_store;
let todo1 = store.create(Todo::new("todo1")).await.unwrap();
let todo2 = store.create(Todo::new("todo2")).await.unwrap();
let todo3 = store.create(Todo::new("todo3")).await.unwrap();

let all_todos = store.list().await.unwrap();
assert_eq!(all_todos, [todo1, todo2, todo3]);

let result = store.delete_all().await.unwrap();
assert_eq!(result, 3);
assert!(store.list().await.unwrap().is_empty());
}
};
}

mod test_crud {

mod test_mysql {
use crate::common::Todo;
use miniorm::prelude::*;
use serial_test::serial;
use sqlx::{MySql, MySqlPool};
use std::error::Error;

pub async fn get_store() -> Result<Store<MySql, Todo>, Box<dyn Error>> {
dotenv::dotenv()?;
let url = std::env::var("MYSQL_URL").expect("missing MYSQL_URL env");
let db = MySqlPool::connect(&url).await?;
let store = miniorm::Store::new(db);
store.recreate_table().await?;
Ok(store)
}

test_todo_crud!(get_store().await.unwrap());
}

mod test_pgstore {
use crate::common::Todo;
use miniorm::prelude::*;
use serial_test::serial;
use sqlx::{PgPool, Postgres};
use std::error::Error;

pub async fn get_store() -> Result<Store<Postgres, Todo>, Box<dyn Error>> {
dotenv::dotenv()?;
let url = std::env::var("POSTGRES_URL").expect("missing POSTGRES_URL env");
let db = PgPool::connect(&url).await?;
let store = miniorm::Store::new(db);
store.recreate_table().await?;
Ok(store)
}

test_todo_crud!(get_store().await.unwrap());
}

mod test_sqlitestore {
use crate::common::Todo;
use miniorm::prelude::*;
use serial_test::serial;
use sqlx::{Sqlite, SqlitePool};
use std::error::Error;

async fn get_store() -> Result<Store<Sqlite, Todo>, Box<dyn Error>> {
let url = ":memory:";
let connection = SqlitePool::connect(url).await?;
let store = Store::new(connection);
store.recreate_table().await?;
Ok(store)
}

test_todo_crud!(get_store().await.unwrap());
}
}

0 comments on commit d7e7475

Please sign in to comment.