Merge pull request #60 from SeaQL/ss/test_suite_refactor

Test suite refactor
This commit is contained in:
Chris Tsang 2021-08-07 18:21:59 +08:00 committed by GitHub
commit 1c73ab0759
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 341 additions and 185 deletions

View File

@ -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]

View File

@ -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")]

View File

@ -63,7 +63,13 @@ where
Some(res) => res,
None => return Ok(0),
};
let num_items = result.try_get::<i32>("", "num_items")? as usize;
let num_items = match self.db {
#[cfg(feature = "sqlx-postgres")]
DatabaseConnection::SqlxPostgresPoolConnection(_) => {
result.try_get::<i64>("", "num_items")? as usize
}
_ => result.try_get::<i32>("", "num_items")? as usize,
};
Ok(num_items)
}

View File

@ -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::<Option<$type>, _>(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::<Option<$type>, _>(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::<Option<$type>, _>(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::<Option<$type>, _>(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::<Option<$type>, _>(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::<Option<$type>, _>(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<Decimal> {
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<Decimal> {
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<Decimal> {
let result: Result<Decimal, _> = 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)
}
},
}
}

View File

@ -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) {

View File

@ -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()),

View File

@ -15,8 +15,8 @@ pub struct Model {
pub id: i32,
pub price: Decimal,
pub quantity: i32,
pub order_id: Option<i32>,
pub cake_id: Option<i32>,
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(),

View File

@ -15,8 +15,8 @@ impl EntityName for Entity {
pub struct Model {
pub id: i32,
pub total: Decimal,
pub bakery_id: Option<i32>,
pub customer_id: Option<i32>,
pub bakery_id: i32,
pub customer_id: i32,
pub placed_at: NaiveDateTime,
}

View File

@ -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,
}
}

View File

@ -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 {
};
}

View File

@ -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::Model> = Cake::find_by_id(lineitem_model.cake_id)
let cake: Option<cake::Model> = 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
);
}

View File

@ -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::Model> = Order::find_by_id(order_insert_res.last_insert_id)
let order: Option<order::Model> = 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::Model> = Customer::find_by_id(order_model.customer_id)
let customer: Option<customer::Model> = 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::Model> = Bakery::find_by_id(order_model.bakery_id)
let bakery: Option<bakery::Model> = Bakery::find_by_id(order_model.bakery_id as i64)
.one(db)
.await
.expect("could not find bakery");

View File

@ -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::Model> = Customer::find_by_id(customer_id.clone().unwrap())
.one(db)
.await
.expect("could not find customer");
let customer: Option<customer::Model> =
Customer::find_by_id(customer_id.clone().unwrap() as i64)
.one(db)
.await
.expect("could not find customer");
assert_eq!(customer, None);
}

View File

@ -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();

View File

@ -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<i32>,
number_orders: Option<i64>,
total_spent: Option<Decimal>,
min_spent: Option<Decimal>,
max_spent: Option<Decimal>,
@ -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()),

View File

@ -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<baker::Model> {
#[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<Decimal>,
cakes_sold_opt: Option<Type>,
}
#[derive(Debug)]
@ -166,18 +179,19 @@ async fn find_baker_least_sales(db: &DatabaseConnection) -> Option<baker::Model>
.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<cake::Model> {
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<cak
.unwrap()
}
#[cfg(any(feature = "sqlx-mysql", feature = "sqlx-postgres"))]
async fn create_order(db: &DatabaseConnection, cake: cake::Model) {
let another_customer = customer::ActiveModel {
name: Set("John".to_owned()),
@ -220,8 +235,8 @@ async fn create_order(db: &DatabaseConnection, cake: cake::Model) {
.expect("could not insert customer");
let order = order::ActiveModel {
bakery_id: Set(Some(cake.bakery_id.unwrap())),
customer_id: Set(Some(another_customer.id.clone().unwrap())),
bakery_id: Set(cake.bakery_id.unwrap()),
customer_id: Set(another_customer.id.clone().unwrap()),
total: Set(dec!(200.00)),
placed_at: Set(Utc::now().naive_utc()),
@ -232,10 +247,10 @@ async fn create_order(db: &DatabaseConnection, cake: cake::Model) {
.expect("could not insert order");
let _lineitem = lineitem::ActiveModel {
cake_id: Set(Some(cake.id)),
cake_id: Set(cake.id),
price: Set(dec!(10.00)),
quantity: Set(300),
order_id: Set(Some(order.id.clone().unwrap())),
order_id: Set(order.id.clone().unwrap()),
..Default::default()
}
.save(db)
@ -243,6 +258,7 @@ async fn create_order(db: &DatabaseConnection, cake: cake::Model) {
.expect("could not insert order");
}
#[cfg(any(feature = "sqlx-mysql", feature = "sqlx-postgres"))]
pub async fn test_delete_bakery(db: &DatabaseConnection) {
let initial_bakeries = Bakery::find().all(db).await.unwrap().len();

View File

@ -1,5 +0,0 @@
use sea_orm::{Database, DatabaseConnection};
pub async fn setup() -> DatabaseConnection {
Database::connect("sqlite::memory:").await.unwrap()
}