diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 9e6effd6..a1b7cb67 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -47,6 +47,8 @@ jobs: sqlite: name: SQLite runs-on: ubuntu-20.04 + env: + DATABASE_URL: "sqlite::memory:" strategy: matrix: # runtime: [async-std-native-tls, async-std-rustls, actix-native-tls, actix-rustls, tokio-native-tls, tokio-rustls] @@ -79,6 +81,8 @@ jobs: mysql: name: MySQL runs-on: ubuntu-20.04 + env: + DATABASE_URL: "mysql://root:@localhost" strategy: matrix: # version: [8.0, 5.7, 5.6] @@ -130,6 +134,8 @@ jobs: mariadb: name: MariaDB runs-on: ubuntu-20.04 + env: + DATABASE_URL: "mysql://root:@localhost" strategy: matrix: # version: [10.6, 10.5, 10.4, 10.3, 10.2] @@ -181,6 +187,8 @@ jobs: postgres: name: Postgres runs-on: ubuntu-20.04 + env: + DATABASE_URL: "postgres://root:root@localhost" strategy: matrix: # version: [13.3, 12.7, 11.12, 10.17, 9.6.22] diff --git a/src/database/connection.rs b/src/database/connection.rs index 4215d8fc..3d4215f5 100644 --- a/src/database/connection.rs +++ b/src/database/connection.rs @@ -39,7 +39,7 @@ impl std::fmt::Debug for DatabaseConnection { #[cfg(feature = "sqlx-mysql")] Self::SqlxMySqlPoolConnection(_) => "SqlxMySqlPoolConnection", #[cfg(feature = "sqlx-postgres")] - Self::SqlxPostgresPoolConnection(_) => "SqlxMySqlPoolConnection", + Self::SqlxPostgresPoolConnection(_) => "SqlxPostgresPoolConnection", #[cfg(feature = "sqlx-sqlite")] Self::SqlxSqlitePoolConnection(_) => "SqlxSqlitePoolConnection", #[cfg(feature = "mock")] diff --git a/src/executor/paginator.rs b/src/executor/paginator.rs index a5aa3445..d44bbc5e 100644 --- a/src/executor/paginator.rs +++ b/src/executor/paginator.rs @@ -63,7 +63,13 @@ where Some(res) => res, None => return Ok(0), }; - let num_items = result.try_get::("", "num_items")? as usize; + let num_items = match self.db { + #[cfg(feature = "sqlx-postgres")] + DatabaseConnection::SqlxPostgresPoolConnection(_) => { + result.try_get::("", "num_items")? as usize + } + _ => result.try_get::("", "num_items")? as usize, + }; Ok(num_items) } diff --git a/src/executor/query.rs b/src/executor/query.rs index a7aba51e..b58c5a2c 100644 --- a/src/executor/query.rs +++ b/src/executor/query.rs @@ -1,4 +1,4 @@ -use crate::DbErr; +use crate::{debug_print, DbErr}; use chrono::NaiveDateTime; use serde_json::Value as Json; use std::fmt; @@ -90,31 +90,28 @@ macro_rules! try_getable_all { #[cfg(feature = "sqlx-mysql")] QueryResultRow::SqlxMySql(row) => { use sqlx::Row; - match row.try_get(column.as_str()) { - Ok(v) => Ok(Some(v)), - Err(_) => Ok(None), - } + row.try_get::, _>(column.as_str()) + .map_err(crate::sqlx_error_to_query_err) } #[cfg(feature = "sqlx-postgres")] QueryResultRow::SqlxPostgres(row) => { use sqlx::Row; - match row.try_get(column.as_str()) { - Ok(v) => Ok(Some(v)), - Err(_) => Ok(None), - } + row.try_get::, _>(column.as_str()) + .map_err(crate::sqlx_error_to_query_err) } #[cfg(feature = "sqlx-sqlite")] QueryResultRow::SqlxSqlite(row) => { use sqlx::Row; - match row.try_get(column.as_str()) { - Ok(v) => Ok(Some(v)), - Err(_) => Ok(None), - } + row.try_get::, _>(column.as_str()) + .map_err(crate::sqlx_error_to_query_err) } #[cfg(feature = "mock")] QueryResultRow::Mock(row) => match row.try_get(column.as_str()) { Ok(v) => Ok(Some(v)), - Err(_) => Ok(None), + Err(e) => { + debug_print!("{:#?}", e.to_string()); + Ok(None) + } }, } } @@ -157,10 +154,8 @@ macro_rules! try_getable_unsigned { #[cfg(feature = "sqlx-mysql")] QueryResultRow::SqlxMySql(row) => { use sqlx::Row; - match row.try_get(column.as_str()) { - Ok(v) => Ok(Some(v)), - Err(_) => Ok(None), - } + row.try_get::, _>(column.as_str()) + .map_err(crate::sqlx_error_to_query_err) } #[cfg(feature = "sqlx-postgres")] QueryResultRow::SqlxPostgres(_) => { @@ -169,15 +164,16 @@ macro_rules! try_getable_unsigned { #[cfg(feature = "sqlx-sqlite")] QueryResultRow::SqlxSqlite(row) => { use sqlx::Row; - match row.try_get(column.as_str()) { - Ok(v) => Ok(Some(v)), - Err(_) => Ok(None), - } + row.try_get::, _>(column.as_str()) + .map_err(crate::sqlx_error_to_query_err) } #[cfg(feature = "mock")] QueryResultRow::Mock(row) => match row.try_get(column.as_str()) { Ok(v) => Ok(Some(v)), - Err(_) => Ok(None), + Err(e) => { + debug_print!("{:#?}", e.to_string()); + Ok(None) + } }, } } @@ -218,10 +214,8 @@ macro_rules! try_getable_mysql { #[cfg(feature = "sqlx-mysql")] QueryResultRow::SqlxMySql(row) => { use sqlx::Row; - match row.try_get(column.as_str()) { - Ok(v) => Ok(Some(v)), - Err(_) => Ok(None), - } + row.try_get::, _>(column.as_str()) + .map_err(crate::sqlx_error_to_query_err) } #[cfg(feature = "sqlx-postgres")] QueryResultRow::SqlxPostgres(_) => { @@ -234,7 +228,10 @@ macro_rules! try_getable_mysql { #[cfg(feature = "mock")] QueryResultRow::Mock(row) => match row.try_get(column.as_str()) { Ok(v) => Ok(Some(v)), - Err(_) => Ok(None), + Err(e) => { + debug_print!("{:#?}", e.to_string()); + Ok(None) + } }, } } @@ -309,7 +306,10 @@ impl TryGetable for Option { use sqlx::Row; match row.try_get(column.as_str()) { Ok(v) => Ok(Some(v)), - Err(_) => Ok(None), + Err(e) => { + debug_print!("{:#?}", e.to_string()); + Ok(None) + } } } #[cfg(feature = "sqlx-postgres")] @@ -317,7 +317,10 @@ impl TryGetable for Option { use sqlx::Row; match row.try_get(column.as_str()) { Ok(v) => Ok(Some(v)), - Err(_) => Ok(None), + Err(e) => { + debug_print!("{:#?}", e.to_string()); + Ok(None) + } } } #[cfg(feature = "sqlx-sqlite")] @@ -325,13 +328,19 @@ impl TryGetable for Option { let result: Result = TryGetable::try_get(res, pre, col); match result { Ok(v) => Ok(Some(v)), - Err(_) => Ok(None), + Err(e) => { + debug_print!("{:#?}", e.to_string()); + Ok(None) + } } } #[cfg(feature = "mock")] QueryResultRow::Mock(row) => match row.try_get(column.as_str()) { Ok(v) => Ok(Some(v)), - Err(_) => Ok(None), + Err(e) => { + debug_print!("{:#?}", e.to_string()); + Ok(None) + } }, } } diff --git a/tests/bakery_chain_tests.rs b/tests/bakery_chain_tests.rs index 8c52ca2a..6410a663 100644 --- a/tests/bakery_chain_tests.rs +++ b/tests/bakery_chain_tests.rs @@ -5,16 +5,20 @@ pub use common::{bakery_chain::*, setup::*, TestContext}; mod crud; -// cargo test --test bakery_chain_tests -- --nocapture -#[async_std::test] -#[cfg(feature = "sqlx-mysql")] +// Run the test locally: +// DATABASE_URL="mysql://root:@localhost" cargo test --features sqlx-mysql,runtime-async-std --test bakery_chain_tests +#[cfg_attr(feature = "runtime-async-std", async_std::test)] +#[cfg_attr(feature = "runtime-actix", actix_rt::test)] +#[cfg_attr(feature = "runtime-tokio", tokio::test)] +#[cfg(any( + feature = "sqlx-mysql", + feature = "sqlx-sqlite", + feature = "sqlx-postgres" +))] async fn main() { - let base_url = "mysql://root:@localhost"; - let db_name = "bakery_chain_schema_crud_tests"; - - let db: DatabaseConnection = common::setup::setup(base_url, db_name).await; - create_entities(&db).await; - common::setup::tear_down(base_url, db_name).await; + let ctx = TestContext::new("bakery_chain_schema_crud_tests").await; + create_entities(&ctx.db).await; + ctx.delete().await; } async fn create_entities(db: &DatabaseConnection) { diff --git a/tests/basic.rs b/tests/basic.rs index 5efe6ea2..af724b10 100644 --- a/tests/basic.rs +++ b/tests/basic.rs @@ -1,20 +1,23 @@ -use sea_orm::{entity::*, error::*, sea_query, tests_cfg::*, DbBackend, DbConn, Statement}; +#[allow(unused_imports)] +use sea_orm::{entity::*, error::*, sea_query, tests_cfg::*, Database, DbConn}; -mod setup; - -// cargo test --test basic -- --nocapture +// DATABASE_URL="sqlite::memory:" cargo test --features sqlx-sqlit,runtime-async-std --test basic #[cfg_attr(feature = "runtime-async-std", async_std::test)] #[cfg_attr(feature = "runtime-actix", actix_rt::test)] #[cfg_attr(feature = "runtime-tokio", tokio::test)] #[cfg(feature = "sqlx-sqlite")] async fn main() { - let db: DbConn = setup::setup().await; + use std::env; + let base_url = env::var("DATABASE_URL").expect("Enviroment variable 'DATABASE_URL' not set"); + + let db: DbConn = Database::connect(&base_url).await.unwrap(); setup_schema(&db).await; crud_cake(&db).await.unwrap(); } +#[cfg(feature = "sqlx-sqlite")] async fn setup_schema(db: &DbConn) { use sea_query::*; @@ -28,14 +31,14 @@ async fn setup_schema(db: &DbConn) { .primary_key(), ) .col(ColumnDef::new(cake::Column::Name).string()) - .build(SqliteQueryBuilder); + .to_owned(); - let result = db - .execute(Statement::from_string(DbBackend::Sqlite, stmt)) - .await; + let builder = db.get_database_backend(); + let result = db.execute(builder.build(&stmt)).await; println!("Create table cake: {:?}", result); } +#[cfg(feature = "sqlx-sqlite")] async fn crud_cake(db: &DbConn) -> Result<(), DbErr> { let apple = cake::ActiveModel { name: Set("Apple Pie".to_owned()), diff --git a/tests/common/bakery_chain/lineitem.rs b/tests/common/bakery_chain/lineitem.rs index 94b15cac..dcdc54ba 100644 --- a/tests/common/bakery_chain/lineitem.rs +++ b/tests/common/bakery_chain/lineitem.rs @@ -15,8 +15,8 @@ pub struct Model { pub id: i32, pub price: Decimal, pub quantity: i32, - pub order_id: Option, - pub cake_id: Option, + pub order_id: i32, + pub cake_id: i32, } #[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] @@ -51,7 +51,7 @@ impl ColumnTrait for Column { fn def(&self) -> ColumnDef { match self { Self::Id => ColumnType::Integer.def(), - Self::Price => ColumnType::Money(Some((19, 4))).def(), + Self::Price => ColumnType::Decimal(Some((19, 4))).def(), Self::Quantity => ColumnType::Integer.def(), Self::OrderId => ColumnType::Integer.def(), Self::CakeId => ColumnType::Integer.def(), diff --git a/tests/common/bakery_chain/order.rs b/tests/common/bakery_chain/order.rs index 41bfb03f..d2eb9641 100644 --- a/tests/common/bakery_chain/order.rs +++ b/tests/common/bakery_chain/order.rs @@ -15,8 +15,8 @@ impl EntityName for Entity { pub struct Model { pub id: i32, pub total: Decimal, - pub bakery_id: Option, - pub customer_id: Option, + pub bakery_id: i32, + pub customer_id: i32, pub placed_at: NaiveDateTime, } diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 72679aad..5729aabb 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -2,6 +2,7 @@ pub mod setup; use sea_orm::DatabaseConnection; pub mod bakery_chain; pub use bakery_chain::*; +use std::env; pub struct TestContext { base_url: String, @@ -10,12 +11,14 @@ pub struct TestContext { } impl TestContext { - pub async fn new(base_url: &str, db_name: &str) -> Self { - let db: DatabaseConnection = setup::setup(base_url, db_name).await; + pub async fn new(test_name: &str) -> Self { + let base_url = + env::var("DATABASE_URL").expect("Enviroment variable 'DATABASE_URL' not set"); + let db: DatabaseConnection = setup::setup(&base_url, test_name).await; Self { - base_url: base_url.to_string(), - db_name: db_name.to_string(), + base_url: base_url, + db_name: test_name.to_string(), db, } } diff --git a/tests/common/setup/mod.rs b/tests/common/setup/mod.rs index 286cd544..7528cf6c 100644 --- a/tests/common/setup/mod.rs +++ b/tests/common/setup/mod.rs @@ -3,24 +3,47 @@ pub mod schema; pub use schema::*; pub async fn setup(base_url: &str, db_name: &str) -> DatabaseConnection { - let url = format!("{}/mysql", base_url); - let db = Database::connect(&url).await.unwrap(); - let _drop_db_result = db - .execute(Statement::from_string( - DatabaseBackend::MySql, - format!("DROP DATABASE IF EXISTS `{}`;", db_name), - )) - .await; + let db = if cfg!(feature = "sqlx-mysql") { + let url = format!("{}/mysql", base_url); + let db = Database::connect(&url).await.unwrap(); + let _drop_db_result = db + .execute(Statement::from_string( + DatabaseBackend::MySql, + format!("DROP DATABASE IF EXISTS `{}`;", db_name), + )) + .await; - let _create_db_result = db - .execute(Statement::from_string( - DatabaseBackend::MySql, - format!("CREATE DATABASE `{}`;", db_name), - )) - .await; + let _create_db_result = db + .execute(Statement::from_string( + DatabaseBackend::MySql, + format!("CREATE DATABASE `{}`;", db_name), + )) + .await; - let url = format!("{}/{}", base_url, db_name); - let db = Database::connect(&url).await.unwrap(); + let url = format!("{}/{}", base_url, db_name); + Database::connect(&url).await.unwrap() + } else if cfg!(feature = "sqlx-postgres") { + let url = format!("{}/postgres", base_url); + let db = Database::connect(&url).await.unwrap(); + let _drop_db_result = db + .execute(Statement::from_string( + DatabaseBackend::Postgres, + format!("DROP DATABASE IF EXISTS \"{}\";", db_name), + )) + .await; + + let _create_db_result = db + .execute(Statement::from_string( + DatabaseBackend::Postgres, + format!("CREATE DATABASE \"{}\";", db_name), + )) + .await; + + let url = format!("{}/{}", base_url, db_name); + Database::connect(&url).await.unwrap() + } else { + Database::connect(base_url).await.unwrap() + }; assert!(schema::create_bakery_table(&db).await.is_ok()); assert!(schema::create_baker_table(&db).await.is_ok()); @@ -33,12 +56,24 @@ pub async fn setup(base_url: &str, db_name: &str) -> DatabaseConnection { } pub async fn tear_down(base_url: &str, db_name: &str) { - let url = format!("{}/mysql", base_url); - let db = Database::connect(&url).await.unwrap(); - let _drop_db_result = db - .execute(Statement::from_string( - DatabaseBackend::MySql, - format!("DROP DATABASE IF EXISTS `{}`;", db_name), - )) - .await; + if cfg!(feature = "sqlx-mysql") { + let url = format!("{}/mysql", base_url); + let db = Database::connect(&url).await.unwrap(); + let _ = db + .execute(Statement::from_string( + DatabaseBackend::MySql, + format!("DROP DATABASE IF EXISTS \"{}\";", db_name), + )) + .await; + } else if cfg!(feature = "sqlx-postgres") { + let url = format!("{}/postgres", base_url); + let db = Database::connect(&url).await.unwrap(); + let _ = db + .execute(Statement::from_string( + DatabaseBackend::Postgres, + format!("DROP DATABASE IF EXISTS \"{}\";", db_name), + )) + .await; + } else { + }; } diff --git a/tests/crud/create_lineitem.rs b/tests/crud/create_lineitem.rs index 4d2b2a14..c82958f9 100644 --- a/tests/crud/create_lineitem.rs +++ b/tests/crud/create_lineitem.rs @@ -70,8 +70,8 @@ pub async fn test_create_lineitem(db: &DbConn) { // Order let order_1 = order::ActiveModel { - bakery_id: Set(Some(bakery_insert_res.last_insert_id as i32)), - customer_id: Set(Some(customer_insert_res.last_insert_id as i32)), + bakery_id: Set(bakery_insert_res.last_insert_id as i32), + customer_id: Set(customer_insert_res.last_insert_id as i32), total: Set(dec!(7.55)), placed_at: Set(Utc::now().naive_utc()), ..Default::default() @@ -83,8 +83,8 @@ pub async fn test_create_lineitem(db: &DbConn) { // Lineitem let lineitem_1 = lineitem::ActiveModel { - cake_id: Set(Some(cake_insert_res.last_insert_id as i32)), - order_id: Set(Some(order_insert_res.last_insert_id as i32)), + cake_id: Set(cake_insert_res.last_insert_id as i32), + order_id: Set(order_insert_res.last_insert_id as i32), price: Set(dec!(7.55)), quantity: Set(1), ..Default::default() @@ -102,9 +102,10 @@ pub async fn test_create_lineitem(db: &DbConn) { assert!(lineitem.is_some()); let lineitem_model = lineitem.unwrap(); + assert_eq!(lineitem_model.price, dec!(7.55)); - let cake: Option = Cake::find_by_id(lineitem_model.cake_id) + let cake: Option = Cake::find_by_id(lineitem_model.cake_id as u64) .one(db) .await .expect("could not find cake"); @@ -119,7 +120,7 @@ pub async fn test_create_lineitem(db: &DbConn) { let order_model = order.unwrap(); assert_eq!( - order_model.customer_id.unwrap(), + order_model.customer_id, customer_insert_res.last_insert_id as i32 ); } diff --git a/tests/crud/create_order.rs b/tests/crud/create_order.rs index e1ec90f5..46ebcf09 100644 --- a/tests/crud/create_order.rs +++ b/tests/crud/create_order.rs @@ -70,8 +70,8 @@ pub async fn test_create_order(db: &DbConn) { // Order let order_1 = order::ActiveModel { - bakery_id: Set(Some(bakery_insert_res.last_insert_id as i32)), - customer_id: Set(Some(customer_insert_res.last_insert_id as i32)), + bakery_id: Set(bakery_insert_res.last_insert_id as i32), + customer_id: Set(customer_insert_res.last_insert_id as i32), total: Set(dec!(15.10)), placed_at: Set(Utc::now().naive_utc()), ..Default::default() @@ -83,8 +83,8 @@ pub async fn test_create_order(db: &DbConn) { // Lineitem let lineitem_1 = lineitem::ActiveModel { - cake_id: Set(Some(cake_insert_res.last_insert_id as i32)), - order_id: Set(Some(order_insert_res.last_insert_id as i32)), + cake_id: Set(cake_insert_res.last_insert_id as i32), + order_id: Set(order_insert_res.last_insert_id as i32), price: Set(dec!(7.55)), quantity: Set(2), ..Default::default() @@ -94,7 +94,7 @@ pub async fn test_create_order(db: &DbConn) { .await .expect("could not insert lineitem"); - let order: Option = Order::find_by_id(order_insert_res.last_insert_id) + let order: Option = Order::find_by_id(order_insert_res.last_insert_id as i32) .one(db) .await .expect("could not find order"); @@ -103,7 +103,7 @@ pub async fn test_create_order(db: &DbConn) { let order_model = order.unwrap(); assert_eq!(order_model.total, dec!(15.10)); - let customer: Option = Customer::find_by_id(order_model.customer_id) + let customer: Option = Customer::find_by_id(order_model.customer_id as u64) .one(db) .await .expect("could not find customer"); @@ -111,7 +111,7 @@ pub async fn test_create_order(db: &DbConn) { let customer_model = customer.unwrap(); assert_eq!(customer_model.name, "Kate"); - let bakery: Option = Bakery::find_by_id(order_model.bakery_id) + let bakery: Option = Bakery::find_by_id(order_model.bakery_id as i64) .one(db) .await .expect("could not find bakery"); diff --git a/tests/crud/updates.rs b/tests/crud/updates.rs index eaf305f7..505e4837 100644 --- a/tests/crud/updates.rs +++ b/tests/crud/updates.rs @@ -130,10 +130,11 @@ pub async fn test_update_deleted_customer(db: &DbConn) { assert_eq!(Customer::find().count(db).await.unwrap(), init_n_customers); - let customer: Option = Customer::find_by_id(customer_id.clone().unwrap()) - .one(db) - .await - .expect("could not find customer"); + let customer: Option = + Customer::find_by_id(customer_id.clone().unwrap() as i64) + .one(db) + .await + .expect("could not find customer"); assert_eq!(customer, None); } diff --git a/tests/query_tests.rs b/tests/query_tests.rs index 466b7b3b..2791f908 100644 --- a/tests/query_tests.rs +++ b/tests/query_tests.rs @@ -1,14 +1,21 @@ -// cargo test --test query_tests -- --nocapture use sea_orm::entity::*; use sea_orm::QueryFilter; pub mod common; pub use common::{bakery_chain::*, setup::*, TestContext}; -#[async_std::test] -#[cfg(feature = "sqlx-mysql")] +// Run the test locally: +// DATABASE_URL="mysql://root:@localhost" cargo test --features sqlx-mysql,runtime-async-std --test query_tests +#[cfg_attr(feature = "runtime-async-std", async_std::test)] +#[cfg_attr(feature = "runtime-actix", actix_rt::test)] +#[cfg_attr(feature = "runtime-tokio", tokio::test)] +#[cfg(any( + feature = "sqlx-mysql", + feature = "sqlx-sqlite", + feature = "sqlx-postgres" +))] pub async fn find_one_with_no_result() { - let ctx = TestContext::new("mysql://root:@localhost", "find_one_with_no_result").await; + let ctx = TestContext::new("find_one_with_no_result").await; let bakery = Bakery::find().one(&ctx.db).await.unwrap(); assert_eq!(bakery, None); @@ -16,10 +23,16 @@ pub async fn find_one_with_no_result() { ctx.delete().await; } -#[async_std::test] -#[cfg(feature = "sqlx-mysql")] +#[cfg_attr(feature = "runtime-async-std", async_std::test)] +#[cfg_attr(feature = "runtime-actix", actix_rt::test)] +#[cfg_attr(feature = "runtime-tokio", tokio::test)] +#[cfg(any( + feature = "sqlx-mysql", + feature = "sqlx-sqlite", + feature = "sqlx-postgres" +))] pub async fn find_one_with_result() { - let ctx = TestContext::new("mysql://root:@localhost", "find_one_with_result").await; + let ctx = TestContext::new("find_one_with_result").await; let bakery = bakery::ActiveModel { name: Set("SeaSide Bakery".to_owned()), @@ -37,10 +50,16 @@ pub async fn find_one_with_result() { ctx.delete().await; } -#[async_std::test] -#[cfg(feature = "sqlx-mysql")] +#[cfg_attr(feature = "runtime-async-std", async_std::test)] +#[cfg_attr(feature = "runtime-actix", actix_rt::test)] +#[cfg_attr(feature = "runtime-tokio", tokio::test)] +#[cfg(any( + feature = "sqlx-mysql", + feature = "sqlx-sqlite", + feature = "sqlx-postgres" +))] pub async fn find_by_id_with_no_result() { - let ctx = TestContext::new("mysql://root:@localhost", "find_by_id_with_no_result").await; + let ctx = TestContext::new("find_by_id_with_no_result").await; let bakery = Bakery::find_by_id(999).one(&ctx.db).await.unwrap(); assert_eq!(bakery, None); @@ -48,10 +67,16 @@ pub async fn find_by_id_with_no_result() { ctx.delete().await; } -#[async_std::test] -#[cfg(feature = "sqlx-mysql")] +#[cfg_attr(feature = "runtime-async-std", async_std::test)] +#[cfg_attr(feature = "runtime-actix", actix_rt::test)] +#[cfg_attr(feature = "runtime-tokio", tokio::test)] +#[cfg(any( + feature = "sqlx-mysql", + feature = "sqlx-sqlite", + feature = "sqlx-postgres" +))] pub async fn find_by_id_with_result() { - let ctx = TestContext::new("mysql://root:@localhost", "find_by_id_with_result").await; + let ctx = TestContext::new("find_by_id_with_result").await; let bakery = bakery::ActiveModel { name: Set("SeaSide Bakery".to_owned()), @@ -73,10 +98,16 @@ pub async fn find_by_id_with_result() { ctx.delete().await; } -#[async_std::test] -#[cfg(feature = "sqlx-mysql")] +#[cfg_attr(feature = "runtime-async-std", async_std::test)] +#[cfg_attr(feature = "runtime-actix", actix_rt::test)] +#[cfg_attr(feature = "runtime-tokio", tokio::test)] +#[cfg(any( + feature = "sqlx-mysql", + feature = "sqlx-sqlite", + feature = "sqlx-postgres" +))] pub async fn find_all_with_no_result() { - let ctx = TestContext::new("mysql://root:@localhost", "find_all_with_no_result").await; + let ctx = TestContext::new("find_all_with_no_result").await; let bakeries = Bakery::find().all(&ctx.db).await.unwrap(); assert_eq!(bakeries.len(), 0); @@ -84,10 +115,16 @@ pub async fn find_all_with_no_result() { ctx.delete().await; } -#[async_std::test] -#[cfg(feature = "sqlx-mysql")] +#[cfg_attr(feature = "runtime-async-std", async_std::test)] +#[cfg_attr(feature = "runtime-actix", actix_rt::test)] +#[cfg_attr(feature = "runtime-tokio", tokio::test)] +#[cfg(any( + feature = "sqlx-mysql", + feature = "sqlx-sqlite", + feature = "sqlx-postgres" +))] pub async fn find_all_with_result() { - let ctx = TestContext::new("mysql://root:@localhost", "find_all_with_result").await; + let ctx = TestContext::new("find_all_with_result").await; let _ = bakery::ActiveModel { name: Set("SeaSide Bakery".to_owned()), @@ -114,10 +151,16 @@ pub async fn find_all_with_result() { ctx.delete().await; } -#[async_std::test] -#[cfg(feature = "sqlx-mysql")] +#[cfg_attr(feature = "runtime-async-std", async_std::test)] +#[cfg_attr(feature = "runtime-actix", actix_rt::test)] +#[cfg_attr(feature = "runtime-tokio", tokio::test)] +#[cfg(any( + feature = "sqlx-mysql", + feature = "sqlx-sqlite", + feature = "sqlx-postgres" +))] pub async fn find_all_filter_no_result() { - let ctx = TestContext::new("mysql://root:@localhost", "find_all_filter_no_result").await; + let ctx = TestContext::new("find_all_filter_no_result").await; let _ = bakery::ActiveModel { name: Set("SeaSide Bakery".to_owned()), @@ -148,10 +191,16 @@ pub async fn find_all_filter_no_result() { ctx.delete().await; } -#[async_std::test] -#[cfg(feature = "sqlx-mysql")] +#[cfg_attr(feature = "runtime-async-std", async_std::test)] +#[cfg_attr(feature = "runtime-actix", actix_rt::test)] +#[cfg_attr(feature = "runtime-tokio", tokio::test)] +#[cfg(any( + feature = "sqlx-mysql", + feature = "sqlx-sqlite", + feature = "sqlx-postgres" +))] pub async fn find_all_filter_with_results() { - let ctx = TestContext::new("mysql://root:@localhost", "find_all_filter_with_results").await; + let ctx = TestContext::new("find_all_filter_with_results").await; let _ = bakery::ActiveModel { name: Set("SeaSide Bakery".to_owned()), @@ -172,7 +221,7 @@ pub async fn find_all_filter_with_results() { .expect("could not insert bakery"); let bakeries = Bakery::find() - .filter(bakery::Column::Name.contains("bakery")) + .filter(bakery::Column::Name.contains("Bakery")) .all(&ctx.db) .await .unwrap(); diff --git a/tests/relational_tests.rs b/tests/relational_tests.rs index 3e019473..85fdee41 100644 --- a/tests/relational_tests.rs +++ b/tests/relational_tests.rs @@ -1,5 +1,3 @@ -// cargo test --test realtional_tests -- --nocapture - use chrono::offset::Utc; use rust_decimal::prelude::*; use rust_decimal_macros::dec; @@ -8,10 +6,18 @@ use sea_orm::{entity::*, query::*, FromQueryResult}; pub mod common; pub use common::{bakery_chain::*, setup::*, TestContext}; -#[async_std::test] -#[cfg(feature = "sqlx-mysql")] +// Run the test locally: +// DATABASE_URL="mysql://root:@localhost" cargo test --features sqlx-mysql,runtime-async-std --test relational_tests +#[cfg_attr(feature = "runtime-async-std", async_std::test)] +#[cfg_attr(feature = "runtime-actix", actix_rt::test)] +#[cfg_attr(feature = "runtime-tokio", tokio::test)] +#[cfg(any( + feature = "sqlx-mysql", + feature = "sqlx-sqlite", + feature = "sqlx-postgres" +))] pub async fn left_join() { - let ctx = TestContext::new("mysql://root:@localhost", "test_left_join").await; + let ctx = TestContext::new("test_left_join").await; let bakery = bakery::ActiveModel { name: Set("SeaSide Bakery".to_owned()), @@ -85,10 +91,12 @@ pub async fn left_join() { ctx.delete().await; } -#[async_std::test] -#[cfg(feature = "sqlx-mysql")] +#[cfg_attr(feature = "runtime-async-std", async_std::test)] +#[cfg_attr(feature = "runtime-actix", actix_rt::test)] +#[cfg_attr(feature = "runtime-tokio", tokio::test)] +#[cfg(any(feature = "sqlx-mysql", feature = "sqlx-postgres"))] pub async fn right_join() { - let ctx = TestContext::new("mysql://root:@localhost", "test_right_join").await; + let ctx = TestContext::new("test_right_join").await; let bakery = bakery::ActiveModel { name: Set("SeaSide Bakery".to_owned()), @@ -116,8 +124,8 @@ pub async fn right_join() { .expect("could not insert customer"); let _order = order::ActiveModel { - bakery_id: Set(Some(bakery.id.clone().unwrap())), - customer_id: Set(Some(customer_kate.id.clone().unwrap())), + bakery_id: Set(bakery.id.clone().unwrap()), + customer_id: Set(customer_kate.id.clone().unwrap()), total: Set(dec!(15.10)), placed_at: Set(Utc::now().naive_utc()), @@ -166,10 +174,16 @@ pub async fn right_join() { ctx.delete().await; } -#[async_std::test] -#[cfg(feature = "sqlx-mysql")] +#[cfg_attr(feature = "runtime-async-std", async_std::test)] +#[cfg_attr(feature = "runtime-actix", actix_rt::test)] +#[cfg_attr(feature = "runtime-tokio", tokio::test)] +#[cfg(any( + feature = "sqlx-mysql", + feature = "sqlx-sqlite", + feature = "sqlx-postgres" +))] pub async fn inner_join() { - let ctx = TestContext::new("mysql://root:@localhost", "test_inner_join").await; + let ctx = TestContext::new("test_inner_join").await; let bakery = bakery::ActiveModel { name: Set("SeaSide Bakery".to_owned()), @@ -197,8 +211,8 @@ pub async fn inner_join() { .expect("could not insert customer"); let kate_order_1 = order::ActiveModel { - bakery_id: Set(Some(bakery.id.clone().unwrap())), - customer_id: Set(Some(customer_kate.id.clone().unwrap())), + bakery_id: Set(bakery.id.clone().unwrap()), + customer_id: Set(customer_kate.id.clone().unwrap()), total: Set(dec!(15.10)), placed_at: Set(Utc::now().naive_utc()), @@ -209,8 +223,8 @@ pub async fn inner_join() { .expect("could not insert order"); let kate_order_2 = order::ActiveModel { - bakery_id: Set(Some(bakery.id.clone().unwrap())), - customer_id: Set(Some(customer_kate.id.clone().unwrap())), + bakery_id: Set(bakery.id.clone().unwrap()), + customer_id: Set(customer_kate.id.clone().unwrap()), total: Set(dec!(100.00)), placed_at: Set(Utc::now().naive_utc()), @@ -251,10 +265,16 @@ pub async fn inner_join() { ctx.delete().await; } -#[async_std::test] -#[cfg(feature = "sqlx-mysql")] +#[cfg_attr(feature = "runtime-async-std", async_std::test)] +#[cfg_attr(feature = "runtime-actix", actix_rt::test)] +#[cfg_attr(feature = "runtime-tokio", tokio::test)] +#[cfg(any( + feature = "sqlx-mysql", + feature = "sqlx-sqlite", + feature = "sqlx-postgres" +))] pub async fn group_by() { - let ctx = TestContext::new("mysql://root:@localhost", "test_group_by").await; + let ctx = TestContext::new("test_group_by").await; let bakery = bakery::ActiveModel { name: Set("SeaSide Bakery".to_owned()), @@ -274,8 +294,8 @@ pub async fn group_by() { .expect("could not insert customer"); let kate_order_1 = order::ActiveModel { - bakery_id: Set(Some(bakery.id.clone().unwrap())), - customer_id: Set(Some(customer_kate.id.clone().unwrap())), + bakery_id: Set(bakery.id.clone().unwrap()), + customer_id: Set(customer_kate.id.clone().unwrap()), total: Set(dec!(99.95)), placed_at: Set(Utc::now().naive_utc()), @@ -286,8 +306,8 @@ pub async fn group_by() { .expect("could not insert order"); let kate_order_2 = order::ActiveModel { - bakery_id: Set(Some(bakery.id.clone().unwrap())), - customer_id: Set(Some(customer_kate.id.clone().unwrap())), + bakery_id: Set(bakery.id.clone().unwrap()), + customer_id: Set(customer_kate.id.clone().unwrap()), total: Set(dec!(200.00)), placed_at: Set(Utc::now().naive_utc()), @@ -300,7 +320,7 @@ pub async fn group_by() { #[derive(Debug, FromQueryResult)] struct SelectResult { name: String, - number_orders: Option, + number_orders: Option, total_spent: Option, min_spent: Option, max_spent: Option, @@ -351,11 +371,17 @@ pub async fn group_by() { ctx.delete().await; } -#[async_std::test] -#[cfg(feature = "sqlx-mysql")] +#[cfg_attr(feature = "runtime-async-std", async_std::test)] +#[cfg_attr(feature = "runtime-actix", actix_rt::test)] +#[cfg_attr(feature = "runtime-tokio", tokio::test)] +#[cfg(any( + feature = "sqlx-mysql", + feature = "sqlx-sqlite", + feature = "sqlx-postgres" +))] pub async fn having() { // customers with orders with total equal to $90 - let ctx = TestContext::new("mysql://root:@localhost", "test_having").await; + let ctx = TestContext::new("test_having").await; let bakery = bakery::ActiveModel { name: Set("SeaSide Bakery".to_owned()), @@ -375,8 +401,8 @@ pub async fn having() { .expect("could not insert customer"); let kate_order_1 = order::ActiveModel { - bakery_id: Set(Some(bakery.id.clone().unwrap())), - customer_id: Set(Some(customer_kate.id.clone().unwrap())), + bakery_id: Set(bakery.id.clone().unwrap()), + customer_id: Set(customer_kate.id.clone().unwrap()), total: Set(dec!(100.00)), placed_at: Set(Utc::now().naive_utc()), @@ -387,8 +413,8 @@ pub async fn having() { .expect("could not insert order"); let _kate_order_2 = order::ActiveModel { - bakery_id: Set(Some(bakery.id.clone().unwrap())), - customer_id: Set(Some(customer_kate.id.clone().unwrap())), + bakery_id: Set(bakery.id.clone().unwrap()), + customer_id: Set(customer_kate.id.clone().unwrap()), total: Set(dec!(12.00)), placed_at: Set(Utc::now().naive_utc()), @@ -407,8 +433,8 @@ pub async fn having() { .expect("could not insert customer"); let _bob_order_1 = order::ActiveModel { - bakery_id: Set(Some(bakery.id.clone().unwrap())), - customer_id: Set(Some(customer_bob.id.clone().unwrap())), + bakery_id: Set(bakery.id.clone().unwrap()), + customer_id: Set(customer_bob.id.clone().unwrap()), total: Set(dec!(50.0)), placed_at: Set(Utc::now().naive_utc()), @@ -419,8 +445,8 @@ pub async fn having() { .expect("could not insert order"); let _bob_order_2 = order::ActiveModel { - bakery_id: Set(Some(bakery.id.clone().unwrap())), - customer_id: Set(Some(customer_bob.id.clone().unwrap())), + bakery_id: Set(bakery.id.clone().unwrap()), + customer_id: Set(customer_bob.id.clone().unwrap()), total: Set(dec!(50.0)), placed_at: Set(Utc::now().naive_utc()), diff --git a/tests/sequential_op_tests.rs b/tests/sequential_op_tests.rs index f13944cc..d734ceac 100644 --- a/tests/sequential_op_tests.rs +++ b/tests/sequential_op_tests.rs @@ -7,10 +7,14 @@ use uuid::Uuid; pub mod common; pub use common::{bakery_chain::*, setup::*, TestContext}; -#[async_std::test] -#[cfg(feature = "sqlx-mysql")] +// Run the test locally: +// DATABASE_URL="mysql://root:@localhost" cargo test --features sqlx-mysql,runtime-async-std --test sequential_op_tests +#[cfg_attr(feature = "runtime-async-std", async_std::test)] +#[cfg_attr(feature = "runtime-actix", actix_rt::test)] +#[cfg_attr(feature = "runtime-tokio", tokio::test)] +#[cfg(any(feature = "sqlx-mysql", feature = "sqlx-postgres"))] pub async fn test_multiple_operations() { - let ctx = TestContext::new("mysql://root:@localhost", "multiple_sequential_operations").await; + let ctx = TestContext::new("multiple_sequential_operations").await; init_setup(&ctx.db).await; let baker_least_sales = find_baker_least_sales(&ctx.db).await.unwrap(); @@ -21,8 +25,11 @@ pub async fn test_multiple_operations() { let baker_least_sales = find_baker_least_sales(&ctx.db).await.unwrap(); assert_eq!(baker_least_sales.name, "Baker 1"); + + ctx.delete().await; } +#[cfg(any(feature = "sqlx-mysql", feature = "sqlx-postgres"))] async fn init_setup(db: &DatabaseConnection) { let bakery = bakery::ActiveModel { name: Set("SeaSide Bakery".to_owned()), @@ -87,8 +94,8 @@ async fn init_setup(db: &DatabaseConnection) { .expect("could not insert customer"); let kate_order_1 = order::ActiveModel { - bakery_id: Set(Some(bakery.id.clone().unwrap())), - customer_id: Set(Some(customer_kate.id.clone().unwrap())), + bakery_id: Set(bakery.id.clone().unwrap()), + customer_id: Set(customer_kate.id.clone().unwrap()), total: Set(dec!(99.95)), placed_at: Set(Utc::now().naive_utc()), @@ -99,10 +106,10 @@ async fn init_setup(db: &DatabaseConnection) { .expect("could not insert order"); let _lineitem = lineitem::ActiveModel { - cake_id: Set(Some(cake_insert_res.last_insert_id as i32)), + cake_id: Set(cake_insert_res.last_insert_id as i32), price: Set(dec!(10.00)), quantity: Set(12), - order_id: Set(Some(kate_order_1.id.clone().unwrap())), + order_id: Set(kate_order_1.id.clone().unwrap()), ..Default::default() } .save(db) @@ -110,10 +117,10 @@ async fn init_setup(db: &DatabaseConnection) { .expect("could not insert order"); let _lineitem2 = lineitem::ActiveModel { - cake_id: Set(Some(cake_insert_res.last_insert_id as i32)), + cake_id: Set(cake_insert_res.last_insert_id as i32), price: Set(dec!(50.00)), quantity: Set(2), - order_id: Set(Some(kate_order_1.id.clone().unwrap())), + order_id: Set(kate_order_1.id.clone().unwrap()), ..Default::default() } .save(db) @@ -121,11 +128,17 @@ async fn init_setup(db: &DatabaseConnection) { .expect("could not insert order"); } +#[cfg(any(feature = "sqlx-mysql", feature = "sqlx-postgres"))] async fn find_baker_least_sales(db: &DatabaseConnection) -> Option { + #[cfg(feature = "sqlx-postgres")] + type Type = i64; + #[cfg(not(feature = "sqlx-postgres"))] + type Type = Decimal; + #[derive(Debug, FromQueryResult)] struct SelectResult { id: i32, - cakes_sold_opt: Option, + cakes_sold_opt: Option, } #[derive(Debug)] @@ -166,18 +179,19 @@ async fn find_baker_least_sales(db: &DatabaseConnection) -> Option .into_iter() .map(|b| LeastSalesBakerResult { id: b.id.clone(), - cakes_sold: b.cakes_sold_opt.unwrap_or(dec!(0)), + cakes_sold: b.cakes_sold_opt.unwrap_or_default().into(), }) .collect(); results.sort_by(|a, b| b.cakes_sold.cmp(&a.cakes_sold)); - Baker::find_by_id(results.last().unwrap().id) + Baker::find_by_id(results.last().unwrap().id as i64) .one(db) .await .unwrap() } +#[cfg(any(feature = "sqlx-mysql", feature = "sqlx-postgres"))] async fn create_cake(db: &DatabaseConnection, baker: baker::Model) -> Option { let new_cake = cake::ActiveModel { name: Set("New Cake".to_owned()), @@ -210,6 +224,7 @@ async fn create_cake(db: &DatabaseConnection, baker: baker::Model) -> Option DatabaseConnection { - Database::connect("sqlite::memory:").await.unwrap() -}