Merge pull request #42 from SeaQL/ss/test_suite

WIP
This commit is contained in:
Chris Tsang 2021-07-26 14:33:29 +08:00 committed by GitHub
commit f11f7e1c19
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 1493 additions and 152 deletions

View File

@ -139,3 +139,58 @@ jobs:
args: >
--all
--features default,sqlx-postgres,runtime-${{ matrix.runtime }}
mysql:
name: MySQL
runs-on: ubuntu-20.04
strategy:
matrix:
# runtime: [async-std-native-tls, async-std-rustls, actix-native-tls, actix-rustls, tokio-native-tls, tokio-rustls]
runtime: [async-std-native-tls]
services:
mysql:
image: mysql:8.0
env:
MYSQL_HOST: 127.0.0.1
MYSQL_DB: mysql
MYSQL_USER: sea
MYSQL_PASSWORD: sea
MYSQL_ALLOW_EMPTY_PASSWORD: yes
MYSQL_ROOT_PASSWORD:
ports:
- "3306:3306"
options: >-
--health-cmd="mysqladmin ping"
--health-interval=10s
--health-timeout=5s
--health-retries=3
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
- uses: actions/cache@v2
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-mysql-${{ matrix.runtime }}-${{ hashFiles('**/Cargo.lock') }}
- uses: actions-rs/cargo@v1
with:
command: build
args: >
--all
--features default,runtime-${{ matrix.runtime }}
- uses: actions-rs/cargo@v1
with:
command: test
args: >
--all
--features default,sqlx-mysql,runtime-${{ matrix.runtime }}

View File

@ -12,18 +12,24 @@ members = [
[package]
name = "sea-orm"
version = "0.1.0"
authors = [ "Chris Tsang <tyt2y7@gmail.com>" ]
authors = ["Chris Tsang <tyt2y7@gmail.com>"]
edition = "2018"
description = "🐚 An async & dynamic ORM for Rust"
license = "MIT OR Apache-2.0"
documentation = "https://docs.rs/sea-orm"
repository = "https://github.com/SeaQL/sea-orm"
categories = [ "database" ]
keywords = [ "orm", "database", "sql", "mysql", "postgres", "sqlite", "async" ]
categories = ["database"]
keywords = ["orm", "database", "sql", "mysql", "postgres", "sqlite", "async"]
publish = false
[package.metadata.docs.rs]
features = ["default", "sqlx-mysql", "sqlx-postgres", "sqlx-sqlite", "runtime-async-std-native-tls"]
features = [
"default",
"sqlx-mysql",
"sqlx-postgres",
"sqlx-sqlite",
"runtime-async-std-native-tls",
]
rustdoc-args = ["--cfg", "docsrs"]
[lib]
@ -36,59 +42,75 @@ chrono = { version = "^0", optional = true }
futures = { version = "^0.3" }
futures-util = { version = "^0.3" }
rust_decimal = { version = "^1", optional = true }
sea-query = { version = "^0.12" }
sea-query = { version = "~0.12.8" }
# sea-query = { path = "../sea-query" }
sea-orm-macros = { path = "sea-orm-macros", optional = true }
sea-orm-codegen = { path = "sea-orm-codegen", optional = true }
serde = { version = "^1.0", features = [ "derive" ] }
serde = { version = "^1.0", features = ["derive"] }
sqlx = { version = "^0.5", optional = true }
strum = { git = "https://github.com/SeaQL/strum.git", branch = "sea-orm", version = "^0.21", features = [ "derive", "sea-orm" ] }
sqlx-core = { version = "^0.5", optional = true }
sqlx-macros = { version = "^0.5", optional = true }
strum = { git = "https://github.com/SeaQL/strum.git", branch = "sea-orm", version = "^0.21", features = [
"derive",
"sea-orm",
] }
serde_json = { version = "^1", optional = true }
uuid = { version = "0.8", features = ["serde", "v4"], optional = true }
[dev-dependencies]
async-std = { version = "^1.9", features = [ "attributes" ] }
async-std = { version = "^1.9", features = ["attributes"] }
tokio = { version = "^1.6", features = ["full"] }
actix-rt = { version = "2.2.0" }
maplit = { version = "^1" }
rust_decimal_macros = { version = "^1" }
sea-orm = { path = ".", features = ["sqlx-json", "sqlx-chrono", "sqlx-decimal", "debug-print"] }
sea-orm = { path = ".", features = ["debug-print"] }
[features]
debug-print = []
default = [ "macros", "codegen", "with-json", "with-chrono", "with-rust_decimal", "mock" ]
macros = [ "sea-orm-macros" ]
codegen = [ "sea-orm-codegen" ]
default = [
"macros",
"codegen",
"with-json",
"with-chrono",
"with-rust_decimal",
"mock",
"with-uuid",
]
macros = ["sea-orm-macros"]
codegen = ["sea-orm-codegen"]
mock = []
with-json = [ "serde_json", "sea-query/with-json" ]
with-chrono = [ "chrono", "sea-query/with-chrono" ]
with-rust_decimal = [ "rust_decimal", "sea-query/with-rust_decimal" ]
sqlx-dep = [ "sqlx" ]
sqlx-json = [ "sqlx/json", "with-json" ]
sqlx-chrono = [ "sqlx/chrono", "with-chrono" ]
sqlx-decimal = [ "sqlx/decimal", "with-rust_decimal" ]
sqlx-mysql = [ "sqlx-dep", "sea-query/sqlx-mysql", "sqlx/mysql" ]
sqlx-postgres = [ "sqlx-dep", "sea-query/sqlx-postgres", "sqlx/postgres" ]
sqlx-sqlite = [ "sqlx-dep", "sea-query/sqlx-sqlite", "sqlx/sqlite" ]
with-json = ["serde_json", "sea-query/with-json", "sqlx-json"]
with-chrono = ["chrono", "sea-query/with-chrono", "sqlx-chrono"]
with-rust_decimal = [
"rust_decimal",
"sea-query/with-rust_decimal",
"sqlx-decimal",
]
with-uuid = [
"uuid",
"sea-query/with-uuid",
"sqlx-core/uuid",
"sqlx-macros/uuid",
]
sqlx-dep = ["sqlx"]
sqlx-json = ["sqlx/json", "with-json"]
sqlx-chrono = ["sqlx/chrono", "with-chrono"]
sqlx-decimal = ["sqlx/decimal", "with-rust_decimal"]
sqlx-mysql = ["sqlx-dep", "sea-query/sqlx-mysql", "sqlx/mysql"]
sqlx-postgres = ["sqlx-dep", "sea-query/sqlx-postgres", "sqlx/postgres"]
sqlx-sqlite = ["sqlx-dep", "sea-query/sqlx-sqlite", "sqlx/sqlite"]
runtime-async-std = []
runtime-async-std-native-tls = [ "sqlx/runtime-async-std-native-tls", "runtime-async-std" ]
runtime-async-std-rustls = [ "sqlx/runtime-async-std-rustls", "runtime-async-std" ]
runtime-async-std-native-tls = [
"sqlx/runtime-async-std-native-tls",
"runtime-async-std",
]
runtime-async-std-rustls = [
"sqlx/runtime-async-std-rustls",
"runtime-async-std",
]
runtime-actix = []
runtime-actix-native-tls = [ "sqlx/runtime-actix-native-tls", "runtime-actix" ]
runtime-actix-rustls = [ "sqlx/runtime-actix-rustls", "runtime-actix" ]
runtime-actix-native-tls = ["sqlx/runtime-actix-native-tls", "runtime-actix"]
runtime-actix-rustls = ["sqlx/runtime-actix-rustls", "runtime-actix"]
runtime-tokio = []
runtime-tokio-native-tls = [ "sqlx/runtime-tokio-native-tls", "runtime-tokio" ]
runtime-tokio-rustls = [ "sqlx/runtime-tokio-rustls", "runtime-tokio" ]
[[test]]
name = "sqlite-basic"
path = "tests/basic.rs"
required-features = ["sqlx-sqlite"]
[[test]]
name = "sqlite"
path = "tests/bakery_chain_tests.rs"
required-features = ["sqlx-sqlite"]
[[test]]
name = "postgres"
path = "tests/pg_tests.rs"
required-features = ["sqlx-postgres"]
runtime-tokio-native-tls = ["sqlx/runtime-tokio-native-tls", "runtime-tokio"]
runtime-tokio-rustls = ["sqlx/runtime-tokio-rustls", "runtime-tokio"]

View File

@ -5,10 +5,10 @@ edition = "2018"
publish = false
[dependencies]
async-std = { version = "^1.9", features = [ "attributes" ] }
async-std = { version = "^1.9", features = ["attributes"] }
sea-orm = { path = "../../" }
sea-orm-codegen = { path = "../../sea-orm-codegen" }
sea-query = { version = "^0.12" }
strum = { version = "^0.20", features = [ "derive" ] }
sea-query = { version = "~0.12.8" }
strum = { version = "^0.20", features = ["derive"] }
serde_json = { version = "^1" }
quote = { version = "^1" }

View File

@ -1,14 +1,14 @@
[package]
name = "sea-orm-codegen"
version = "0.1.0"
authors = [ "Billy Chan <ccw.billy.123@gmail.com>" ]
authors = ["Billy Chan <ccw.billy.123@gmail.com>"]
edition = "2018"
description = ""
license = "MIT OR Apache-2.0"
documentation = "https://docs.rs/sea-orm"
repository = "https://github.com/SeaQL/sea-orm"
categories = [ "database" ]
keywords = [ "orm", "database", "sql", "mysql", "postgres", "sqlite" ]
categories = ["database"]
keywords = ["orm", "database", "sql", "mysql", "postgres", "sqlite"]
publish = false
[lib]
@ -16,16 +16,25 @@ name = "sea_orm_codegen"
path = "src/lib.rs"
[dependencies]
sea-schema = { version = "^0.2", default-features = false, features = [ "sqlx-mysql", "discovery", "writer" ] }
sea-query = { version = "^0.12" }
sqlx = { version = "^0.5", default-features = false, features = [ "mysql" ] }
syn = { version = "^1", default-features = false, features = [ "derive", "parsing", "proc-macro", "printing" ] }
sea-schema = { version = "^0.2", default-features = false, features = [
"sqlx-mysql",
"discovery",
"writer",
] }
sea-query = { version = "~0.12.8" }
sqlx = { version = "^0.5", default-features = false, features = ["mysql"] }
syn = { version = "^1", default-features = false, features = [
"derive",
"parsing",
"proc-macro",
"printing",
] }
quote = "^1"
heck = "^0.3"
proc-macro2 = "^1"
[dev-dependencies]
async-std = { version = "^1.9", features = [ "attributes" ] }
async-std = { version = "^1.9", features = ["attributes"] }
sea-orm = { path = "../" }
[features]

View File

@ -32,6 +32,7 @@ impl Column {
| ColumnType::Date
| ColumnType::Json
| ColumnType::JsonBinary
| ColumnType::Uuid
| ColumnType::Custom(_) => "String",
ColumnType::TinyInteger(_) => "i8",
ColumnType::SmallInteger(_) => "i16",
@ -83,6 +84,7 @@ impl Column {
},
ColumnType::Json => quote! { ColumnType::Json.def() },
ColumnType::JsonBinary => quote! { ColumnType::JsonBinary.def() },
ColumnType::Uuid => quote! { ColumnType::Uuid.def() },
ColumnType::Custom(s) => {
let s = s.to_string();
quote! { ColumnType::Custom(#s.to_owned()).def() }

View File

@ -31,6 +31,7 @@ pub enum ColumnType {
Json,
JsonBinary,
Custom(String),
Uuid,
}
macro_rules! bind_oper {
@ -270,6 +271,7 @@ impl From<ColumnType> for sea_query::ColumnType {
ColumnType::Custom(s) => {
sea_query::ColumnType::Custom(sea_query::SeaRc::new(sea_query::Alias::new(&s)))
}
ColumnType::Uuid => sea_query::ColumnType::Uuid,
}
}
}
@ -297,6 +299,7 @@ impl From<sea_query::ColumnType> for ColumnType {
sea_query::ColumnType::Json => Self::Json,
sea_query::ColumnType::JsonBinary => Self::JsonBinary,
sea_query::ColumnType::Custom(s) => Self::Custom(s.to_string()),
sea_query::ColumnType::Uuid => Self::Uuid,
}
}
}

View File

@ -1,5 +1,6 @@
use crate::DbErr;
use chrono::NaiveDateTime;
use serde_json::Value as Json;
use std::fmt;
#[derive(Debug)]
@ -254,6 +255,13 @@ try_getable_all!(f32);
try_getable_all!(f64);
try_getable_all!(String);
try_getable_all!(NaiveDateTime);
try_getable_all!(Json);
#[cfg(feature = "with-uuid")]
use uuid::Uuid;
#[cfg(feature = "with-uuid")]
try_getable_all!(Uuid);
#[cfg(feature = "with-rust_decimal")]
use rust_decimal::Decimal;

View File

@ -1 +0,0 @@
<mxfile host="Electron" modified="2021-06-30T11:15:47.471Z" agent="5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/14.6.13 Chrome/89.0.4389.128 Electron/12.0.7 Safari/537.36" etag="CL5AmeQl6dz-rdIIukNg" version="14.6.13" type="device"><diagram id="R2lEEEUBdFMjLlhIrx00" name="Page-1">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</diagram></mxfile>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

View File

@ -1,37 +1,34 @@
use sea_orm::DbConn;
use sea_orm::DatabaseConnection;
pub mod common;
pub use common::{bakery_chain::*, setup::*, TestContext};
pub mod bakery_chain;
mod setup;
pub use bakery_chain::*;
mod crud;
mod schema;
// cargo test --test bakery_chain_tests -- --nocapture
#[cfg_attr(feature = "runtime-async-std", async_std::main)]
#[cfg_attr(feature = "runtime-actix", actix_rt::main)]
#[cfg_attr(feature = "runtime-tokio", tokio::main)]
#[cfg(feature = "sqlx-sqlite")]
#[async_std::test]
#[cfg(feature = "sqlx-mysql")]
async fn main() {
let db: DbConn = setup::setup().await;
setup_schema(&db).await;
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;
}
async fn setup_schema(db: &DbConn) {
assert!(schema::create_bakery_table(db).await.is_ok());
assert!(schema::create_baker_table(db).await.is_ok());
assert!(schema::create_customer_table(db).await.is_ok());
assert!(schema::create_order_table(db).await.is_ok());
assert!(schema::create_lineitem_table(db).await.is_ok());
assert!(schema::create_cake_table(db).await.is_ok());
assert!(schema::create_cakes_bakers_table(db).await.is_ok());
}
async fn create_entities(db: &DbConn) {
async fn create_entities(db: &DatabaseConnection) {
crud::test_create_bakery(db).await;
crud::test_create_baker(db).await;
crud::create_baker::test_create_baker(db).await;
crud::test_create_customer(db).await;
crud::create_cake::test_create_cake(db).await;
crud::create_lineitem::test_create_lineitem(db).await;
crud::create_order::test_create_order(db).await;
crud::updates::test_update_cake(db).await;
crud::updates::test_update_bakery(db).await;
crud::updates::test_update_deleted_customer(db).await;
crud::deletes::test_delete_cake(db).await;
crud::deletes::test_delete_bakery(db).await;
}

View File

@ -3,9 +3,9 @@ use sea_orm::{entity::*, error::*, sea_query, tests_cfg::*, DbBackend, DbConn, S
mod setup;
// cargo test --test basic -- --nocapture
#[cfg_attr(feature = "runtime-async-std", async_std::main)]
#[cfg_attr(feature = "runtime-actix", actix_rt::main)]
#[cfg_attr(feature = "runtime-tokio", tokio::main)]
#[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;

View File

@ -1,4 +1,5 @@
use sea_orm::entity::prelude::*;
use serde_json::Value as Json;
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
pub struct Entity;
@ -13,6 +14,7 @@ impl EntityName for Entity {
pub struct Model {
pub id: i32,
pub name: String,
pub contact_details: Json,
pub bakery_id: Option<i32>,
}
@ -20,6 +22,7 @@ pub struct Model {
pub enum Column {
Id,
Name,
ContactDetails,
BakeryId,
}
@ -46,6 +49,7 @@ impl ColumnTrait for Column {
match self {
Self::Id => ColumnType::Integer.def(),
Self::Name => ColumnType::String(None).def(),
Self::ContactDetails => ColumnType::Json.def(),
Self::BakeryId => ColumnType::Integer.def(),
}
}

View File

@ -0,0 +1 @@
<mxfile host="Electron" modified="2021-07-24T10:17:39.768Z" agent="5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/14.6.13 Chrome/89.0.4389.128 Electron/12.0.7 Safari/537.36" etag="-nL14gemvUpu6Q8vDMJo" version="14.6.13" type="device"><diagram id="R2lEEEUBdFMjLlhIrx00" name="Page-1">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</diagram></mxfile>

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

View File

@ -1,5 +1,6 @@
use rust_decimal::prelude::*;
use sea_orm::entity::prelude::*;
use uuid::Uuid;
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
pub struct Entity;
@ -17,6 +18,7 @@ pub struct Model {
pub price: Decimal,
pub bakery_id: Option<i32>,
pub gluten_free: bool,
pub serial: Uuid,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
@ -26,6 +28,7 @@ pub enum Column {
Price,
BakeryId,
GlutenFree,
Serial,
}
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
@ -55,6 +58,7 @@ impl ColumnTrait for Column {
Self::Price => ColumnType::Decimal(Some((19, 4))).def(),
Self::BakeryId => ColumnType::Integer.def(),
Self::GlutenFree => ColumnType::Boolean.def(),
Self::Serial => ColumnType::String(None).def(),
}
}
}

View File

@ -13,7 +13,7 @@ impl EntityName for Entity {
pub struct Model {
pub id: i32,
pub name: String,
pub notes: String,
pub notes: Option<String>,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]

26
tests/common/mod.rs Normal file
View File

@ -0,0 +1,26 @@
pub mod setup;
use sea_orm::DatabaseConnection;
pub mod bakery_chain;
pub use bakery_chain::*;
pub struct TestContext {
base_url: String,
db_name: String,
pub db: DatabaseConnection,
}
impl TestContext {
pub async fn new(base_url: &str, db_name: &str) -> Self {
let db: DatabaseConnection = setup::setup(base_url, db_name).await;
Self {
base_url: base_url.to_string(),
db_name: db_name.to_string(),
db,
}
}
pub async fn delete(&self) {
setup::tear_down(&self.base_url, &self.db_name).await;
}
}

44
tests/common/setup/mod.rs Normal file
View File

@ -0,0 +1,44 @@
use sea_orm::{Database, DatabaseBackend, DatabaseConnection, Statement};
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 _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();
assert!(schema::create_bakery_table(&db).await.is_ok());
assert!(schema::create_baker_table(&db).await.is_ok());
assert!(schema::create_customer_table(&db).await.is_ok());
assert!(schema::create_order_table(&db).await.is_ok());
assert!(schema::create_cake_table(&db).await.is_ok());
assert!(schema::create_cakes_bakers_table(&db).await.is_ok());
assert!(schema::create_lineitem_table(&db).await.is_ok());
db
}
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;
}

View File

@ -1,7 +1,7 @@
use sea_orm::{error::*, sea_query, DbConn, ExecResult};
use sea_query::{ColumnDef, ForeignKey, ForeignKeyAction, Index, TableCreateStatement};
pub use super::bakery_chain::*;
pub use super::super::bakery_chain::*;
async fn create_table(db: &DbConn, stmt: &TableCreateStatement) -> Result<ExecResult, DbErr> {
let builder = db.get_database_backend();
@ -20,7 +20,7 @@ pub async fn create_bakery_table(db: &DbConn) -> Result<ExecResult, DbErr> {
.primary_key(),
)
.col(ColumnDef::new(bakery::Column::Name).string())
.col(ColumnDef::new(bakery::Column::ProfitMargin).float())
.col(ColumnDef::new(bakery::Column::ProfitMargin).double())
.to_owned();
create_table(db, &stmt).await
@ -38,6 +38,7 @@ pub async fn create_baker_table(db: &DbConn) -> Result<ExecResult, DbErr> {
.primary_key(),
)
.col(ColumnDef::new(baker::Column::Name).string())
.col(ColumnDef::new(baker::Column::ContactDetails).json())
.col(ColumnDef::new(baker::Column::BakeryId).integer())
.foreign_key(
ForeignKey::create()
@ -81,7 +82,7 @@ pub async fn create_order_table(db: &DbConn) -> Result<ExecResult, DbErr> {
.auto_increment()
.primary_key(),
)
.col(ColumnDef::new(order::Column::Total).float())
.col(ColumnDef::new(order::Column::Total).decimal_len(19, 4))
.col(ColumnDef::new(order::Column::BakeryId).integer().not_null())
.col(
ColumnDef::new(order::Column::CustomerId)
@ -125,7 +126,7 @@ pub async fn create_lineitem_table(db: &DbConn) -> Result<ExecResult, DbErr> {
.auto_increment()
.primary_key(),
)
.col(ColumnDef::new(lineitem::Column::Price).decimal())
.col(ColumnDef::new(lineitem::Column::Price).decimal_len(19, 4))
.col(ColumnDef::new(lineitem::Column::Quantity).integer())
.col(
ColumnDef::new(lineitem::Column::OrderId)
@ -194,7 +195,7 @@ pub async fn create_cake_table(db: &DbConn) -> Result<ExecResult, DbErr> {
.primary_key(),
)
.col(ColumnDef::new(cake::Column::Name).string())
.col(ColumnDef::new(cake::Column::Price).float())
.col(ColumnDef::new(cake::Column::Price).decimal_len(19, 4))
.col(ColumnDef::new(cake::Column::BakeryId).integer().not_null())
.foreign_key(
ForeignKey::create()
@ -205,6 +206,7 @@ pub async fn create_cake_table(db: &DbConn) -> Result<ExecResult, DbErr> {
.on_update(ForeignKeyAction::Cascade),
)
.col(ColumnDef::new(cake::Column::GlutenFree).boolean())
.col(ColumnDef::new(cake::Column::Serial).uuid())
.to_owned();
create_table(db, &stmt).await

View File

@ -0,0 +1,79 @@
pub use super::*;
use serde::{Deserialize, Serialize};
pub async fn test_create_baker(db: &DbConn) {
let seaside_bakery = bakery::ActiveModel {
name: Set("SeaSide Bakery".to_owned()),
profit_margin: Set(10.4),
..Default::default()
};
let bakery_insert_res: InsertResult = Bakery::insert(seaside_bakery)
.exec(db)
.await
.expect("could not insert bakery");
#[derive(Serialize, Deserialize)]
struct ContactDetails {
mobile: String,
home: String,
address: String,
}
let baker_bob_contact = ContactDetails {
mobile: "+61424000000".to_owned(),
home: "0395555555".to_owned(),
address: "12 Test St, Testville, Vic, Australia".to_owned(),
};
let baker_bob = baker::ActiveModel {
name: Set("Baker Bob".to_owned()),
contact_details: Set(serde_json::json!(baker_bob_contact)),
bakery_id: Set(Some(bakery_insert_res.last_insert_id as i32)),
..Default::default()
};
let res: InsertResult = Baker::insert(baker_bob)
.exec(db)
.await
.expect("could not insert baker");
let baker: Option<baker::Model> = Baker::find_by_id(res.last_insert_id)
.one(db)
.await
.expect("could not find baker");
assert!(baker.is_some());
let baker_model = baker.unwrap();
assert_eq!(baker_model.name, "Baker Bob");
assert_eq!(
baker_model.contact_details["mobile"],
baker_bob_contact.mobile
);
assert_eq!(baker_model.contact_details["home"], baker_bob_contact.home);
assert_eq!(
baker_model.contact_details["address"],
baker_bob_contact.address
);
assert_eq!(
baker_model
.find_related(Bakery)
.one(db)
.await
.expect("Bakery not found")
.unwrap()
.name,
"SeaSide Bakery"
);
let bakery: Option<bakery::Model> = Bakery::find_by_id(bakery_insert_res.last_insert_id)
.one(db)
.await
.unwrap();
let related_bakers: Vec<baker::Model> = bakery
.unwrap()
.find_related(Baker)
.all(db)
.await
.expect("could not find related bakers");
assert_eq!(related_bakers.len(), 1);
assert_eq!(related_bakers[0].name, "Baker Bob")
}

View File

@ -1,5 +1,6 @@
pub use super::*;
use rust_decimal_macros::dec;
use uuid::Uuid;
pub async fn test_create_cake(db: &DbConn) {
let seaside_bakery = bakery::ActiveModel {
@ -14,6 +15,11 @@ pub async fn test_create_cake(db: &DbConn) {
let baker_bob = baker::ActiveModel {
name: Set("Baker Bob".to_owned()),
contact_details: Set(serde_json::json!({
"mobile": "+61424000000",
"home": "0395555555",
"address": "12 Test St, Testville, Vic, Australia"
})),
bakery_id: Set(Some(bakery_insert_res.last_insert_id as i32)),
..Default::default()
};
@ -21,11 +27,13 @@ pub async fn test_create_cake(db: &DbConn) {
.exec(db)
.await
.expect("could not insert baker");
let uuid = Uuid::new_v4();
let mud_cake = cake::ActiveModel {
name: Set("Mud Cake".to_owned()),
price: Set(dec!(10.25)),
gluten_free: Set(false),
serial: Set(uuid),
bakery_id: Set(Some(bakery_insert_res.last_insert_id as i32)),
..Default::default()
};
@ -65,6 +73,7 @@ pub async fn test_create_cake(db: &DbConn) {
.name,
"SeaSide Bakery"
);
assert_eq!(cake_model.serial, uuid);
let related_bakers: Vec<baker::Model> = cake_model
.find_related(Baker)

View File

@ -1,6 +1,7 @@
pub use super::*;
use chrono::offset::Utc;
use rust_decimal_macros::dec;
use uuid::Uuid;
pub async fn test_create_lineitem(db: &DbConn) {
// Bakery
@ -17,6 +18,11 @@ pub async fn test_create_lineitem(db: &DbConn) {
// Baker
let baker_bob = baker::ActiveModel {
name: Set("Baker Bob".to_owned()),
contact_details: Set(serde_json::json!({
"mobile": "+61424000000",
"home": "0395555555",
"address": "12 Test St, Testville, Vic, Australia"
})),
bakery_id: Set(Some(bakery_insert_res.last_insert_id as i32)),
..Default::default()
};
@ -30,6 +36,7 @@ pub async fn test_create_lineitem(db: &DbConn) {
name: Set("Mud Cake".to_owned()),
price: Set(dec!(10.25)),
gluten_free: Set(false),
serial: Set(Uuid::new_v4()),
bakery_id: Set(Some(bakery_insert_res.last_insert_id as i32)),
..Default::default()
};
@ -53,7 +60,7 @@ pub async fn test_create_lineitem(db: &DbConn) {
// Customer
let customer_kate = customer::ActiveModel {
name: Set("Kate".to_owned()),
notes: Set("Loves cheese cake".to_owned()),
notes: Set(Some("Loves cheese cake".to_owned())),
..Default::default()
};
let customer_insert_res: InsertResult = Customer::insert(customer_kate)
@ -65,6 +72,7 @@ pub async fn test_create_lineitem(db: &DbConn) {
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)),
total: Set(dec!(7.55)),
placed_at: Set(Utc::now().naive_utc()),
..Default::default()
};
@ -78,6 +86,7 @@ pub async fn test_create_lineitem(db: &DbConn) {
cake_id: Set(Some(cake_insert_res.last_insert_id as i32)),
order_id: Set(Some(order_insert_res.last_insert_id as i32)),
price: Set(dec!(7.55)),
quantity: Set(1),
..Default::default()
};
let lineitem_insert_res: InsertResult = Lineitem::insert(lineitem_1)

View File

@ -1,6 +1,7 @@
pub use super::*;
use chrono::offset::Utc;
use rust_decimal_macros::dec;
use uuid::Uuid;
pub async fn test_create_order(db: &DbConn) {
// Bakery
@ -17,6 +18,11 @@ pub async fn test_create_order(db: &DbConn) {
// Baker
let baker_bob = baker::ActiveModel {
name: Set("Baker Bob".to_owned()),
contact_details: Set(serde_json::json!({
"mobile": "+61424000000",
"home": "0395555555",
"address": "12 Test St, Testville, Vic, Australia"
})),
bakery_id: Set(Some(bakery_insert_res.last_insert_id as i32)),
..Default::default()
};
@ -30,6 +36,7 @@ pub async fn test_create_order(db: &DbConn) {
name: Set("Mud Cake".to_owned()),
price: Set(dec!(10.25)),
gluten_free: Set(false),
serial: Set(Uuid::new_v4()),
bakery_id: Set(Some(bakery_insert_res.last_insert_id as i32)),
..Default::default()
};
@ -53,7 +60,7 @@ pub async fn test_create_order(db: &DbConn) {
// Customer
let customer_kate = customer::ActiveModel {
name: Set("Kate".to_owned()),
notes: Set("Loves cheese cake".to_owned()),
notes: Set(Some("Loves cheese cake".to_owned())),
..Default::default()
};
let customer_insert_res: InsertResult = Customer::insert(customer_kate)

61
tests/crud/deletes.rs Normal file
View File

@ -0,0 +1,61 @@
pub use super::*;
use rust_decimal_macros::dec;
use uuid::Uuid;
pub async fn test_delete_cake(db: &DbConn) {
let initial_cakes = Cake::find().all(db).await.unwrap().len();
let seaside_bakery = bakery::ActiveModel {
name: Set("SeaSide Bakery".to_owned()),
profit_margin: Set(10.4),
..Default::default()
};
let bakery_insert_res: InsertResult = Bakery::insert(seaside_bakery)
.exec(db)
.await
.expect("could not insert bakery");
let mud_cake = cake::ActiveModel {
name: Set("Mud Cake".to_owned()),
price: Set(dec!(10.25)),
gluten_free: Set(false),
serial: Set(Uuid::new_v4()),
bakery_id: Set(Some(bakery_insert_res.last_insert_id as i32)),
..Default::default()
};
let cake = mud_cake.save(db).await.expect("could not insert cake");
let cakes = Cake::find().all(db).await.unwrap();
assert_eq!(cakes.len(), initial_cakes + 1);
let _result = cake.delete(db).await.expect("failed to delete cake");
let cakes = Cake::find().all(db).await.unwrap();
assert_eq!(cakes.len(), initial_cakes);
}
pub async fn test_delete_bakery(db: &DbConn) {
let initial_bakeries = Bakery::find().all(db).await.unwrap().len();
let bakery = bakery::ActiveModel {
name: Set("SeaSide Bakery".to_owned()),
profit_margin: Set(10.4),
..Default::default()
}
.save(db)
.await
.expect("could not insert bakery");
assert_eq!(
Bakery::find().all(db).await.unwrap().len(),
initial_bakeries + 1
);
let _result = bakery.delete(db).await.expect("failed to delete bakery");
assert_eq!(
Bakery::find().all(db).await.unwrap().len(),
initial_bakeries
);
}

View File

@ -1,10 +1,13 @@
use sea_orm::{entity::*, DbConn, InsertResult};
pub use super::bakery_chain::*;
pub use super::common::bakery_chain::*;
pub mod create_baker;
pub mod create_cake;
pub mod create_lineitem;
pub mod create_order;
pub mod deletes;
pub mod updates;
pub async fn test_create_bakery(db: &DbConn) {
let seaside_bakery = bakery::ActiveModel {
@ -28,65 +31,10 @@ pub async fn test_create_bakery(db: &DbConn) {
assert_eq!(bakery_model.profit_margin, 10.4);
}
pub async fn test_create_baker(db: &DbConn) {
let seaside_bakery = bakery::ActiveModel {
name: Set("SeaSide Bakery".to_owned()),
profit_margin: Set(10.4),
..Default::default()
};
let bakery_insert_res: InsertResult = Bakery::insert(seaside_bakery)
.exec(db)
.await
.expect("could not insert bakery");
let baker_bob = baker::ActiveModel {
name: Set("Baker Bob".to_owned()),
bakery_id: Set(Some(bakery_insert_res.last_insert_id as i32)),
..Default::default()
};
let res: InsertResult = Baker::insert(baker_bob)
.exec(db)
.await
.expect("could not insert baker");
let baker: Option<baker::Model> = Baker::find_by_id(res.last_insert_id)
.one(db)
.await
.expect("could not find baker");
assert!(baker.is_some());
let baker_model = baker.unwrap();
assert_eq!(baker_model.name, "Baker Bob");
assert_eq!(
baker_model
.find_related(Bakery)
.one(db)
.await
.expect("Bakery not found")
.unwrap()
.name,
"SeaSide Bakery"
);
let bakery: Option<bakery::Model> = Bakery::find_by_id(bakery_insert_res.last_insert_id)
.one(db)
.await
.unwrap();
let related_bakers: Vec<baker::Model> = bakery
.unwrap()
.find_related(Baker)
.all(db)
.await
.expect("could not find related bakers");
assert_eq!(related_bakers.len(), 1);
assert_eq!(related_bakers[0].name, "Baker Bob")
}
pub async fn test_create_customer(db: &DbConn) {
let customer_kate = customer::ActiveModel {
name: Set("Kate".to_owned()),
notes: Set("Loves cheese cake".to_owned()),
notes: Set(Some("Loves cheese cake".to_owned())),
..Default::default()
};
let res: InsertResult = Customer::insert(customer_kate)
@ -102,5 +50,5 @@ pub async fn test_create_customer(db: &DbConn) {
assert!(customer.is_some());
let customer_model = customer.unwrap();
assert_eq!(customer_model.name, "Kate");
assert_eq!(customer_model.notes, "Loves cheese cake");
assert_eq!(customer_model.notes, Some("Loves cheese cake".to_owned()));
}

139
tests/crud/updates.rs Normal file
View File

@ -0,0 +1,139 @@
pub use super::*;
use rust_decimal_macros::dec;
use uuid::Uuid;
pub async fn test_update_cake(db: &DbConn) {
let seaside_bakery = bakery::ActiveModel {
name: Set("SeaSide Bakery".to_owned()),
profit_margin: Set(10.4),
..Default::default()
};
let bakery_insert_res: InsertResult = Bakery::insert(seaside_bakery)
.exec(db)
.await
.expect("could not insert bakery");
let mud_cake = cake::ActiveModel {
name: Set("Mud Cake".to_owned()),
price: Set(dec!(10.25)),
gluten_free: Set(false),
serial: Set(Uuid::new_v4()),
bakery_id: Set(Some(bakery_insert_res.last_insert_id as i32)),
..Default::default()
};
let cake_insert_res: InsertResult = Cake::insert(mud_cake)
.exec(db)
.await
.expect("could not insert cake");
let cake: Option<cake::Model> = Cake::find_by_id(cake_insert_res.last_insert_id)
.one(db)
.await
.expect("could not find cake");
assert!(cake.is_some());
let cake_model = cake.unwrap();
assert_eq!(cake_model.name, "Mud Cake");
assert_eq!(cake_model.price, dec!(10.25));
assert_eq!(cake_model.gluten_free, false);
let mut cake_am: cake::ActiveModel = cake_model.into();
cake_am.name = Set("Extra chocolate mud cake".to_owned());
cake_am.price = Set(dec!(20.00));
let _cake_update_res: cake::ActiveModel = Cake::update(cake_am)
.exec(db)
.await
.expect("could not update cake");
let cake: Option<cake::Model> = Cake::find_by_id(cake_insert_res.last_insert_id)
.one(db)
.await
.expect("could not find cake");
let cake_model = cake.unwrap();
assert_eq!(cake_model.name, "Extra chocolate mud cake");
assert_eq!(cake_model.price, dec!(20.00));
}
pub async fn test_update_bakery(db: &DbConn) {
let seaside_bakery = bakery::ActiveModel {
name: Set("SeaSide Bakery".to_owned()),
profit_margin: Set(10.4),
..Default::default()
};
let bakery_insert_res: InsertResult = Bakery::insert(seaside_bakery)
.exec(db)
.await
.expect("could not insert bakery");
let bakery: Option<bakery::Model> = Bakery::find_by_id(bakery_insert_res.last_insert_id)
.one(db)
.await
.expect("could not find bakery");
assert!(bakery.is_some());
let bakery_model = bakery.unwrap();
assert_eq!(bakery_model.name, "SeaSide Bakery");
assert_eq!(bakery_model.profit_margin, 10.4);
let mut bakery_am: bakery::ActiveModel = bakery_model.into();
bakery_am.name = Set("SeaBreeze Bakery".to_owned());
bakery_am.profit_margin = Set(12.00);
let _bakery_update_res: bakery::ActiveModel = Bakery::update(bakery_am)
.exec(db)
.await
.expect("could not update bakery");
let bakery: Option<bakery::Model> = Bakery::find_by_id(bakery_insert_res.last_insert_id)
.one(db)
.await
.expect("could not find bakery");
let bakery_model = bakery.unwrap();
assert_eq!(bakery_model.name, "SeaBreeze Bakery");
assert_eq!(bakery_model.profit_margin, 12.00);
}
pub async fn test_update_deleted_customer(db: &DbConn) {
let init_n_customers = Customer::find().count(db).await.unwrap();
let customer = customer::ActiveModel {
name: Set("John".to_owned()),
notes: Set(None),
..Default::default()
}
.save(db)
.await
.expect("could not insert customer");
assert_eq!(
Customer::find().count(db).await.unwrap(),
init_n_customers + 1
);
let customer_id = customer.id.clone();
let _ = customer.delete(db).await;
assert_eq!(Customer::find().count(db).await.unwrap(), init_n_customers);
let customer = customer::ActiveModel {
id: customer_id.clone(),
name: Set("John 2".to_owned()),
..Default::default()
};
let _customer_update_res: customer::ActiveModel = Customer::update(customer)
.exec(db)
.await
.expect("could not update customer");
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");
assert_eq!(customer, None);
}

View File

@ -3,14 +3,15 @@ use sea_orm::{
Statement,
};
pub mod bakery_chain;
pub use bakery_chain::*;
pub mod common;
pub use common::bakery_chain::*;
use sea_query::{ColumnDef, TableCreateStatement};
// cargo test --test pg_tests -- --nocapture
#[cfg_attr(feature = "runtime-async-std", async_std::main)]
#[cfg_attr(feature = "runtime-actix", actix_rt::main)]
#[cfg_attr(feature = "runtime-tokio", tokio::main)]
#[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-postgres")]
async fn main() {
let base_url = "postgres://root:root@localhost";

183
tests/query_tests.rs Normal file
View File

@ -0,0 +1,183 @@
// 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")]
pub async fn find_one_with_no_result() {
let ctx = TestContext::new("mysql://root:@localhost", "find_one_with_no_result").await;
let bakery = Bakery::find().one(&ctx.db).await.unwrap();
assert_eq!(bakery, None);
ctx.delete().await;
}
#[async_std::test]
#[cfg(feature = "sqlx-mysql")]
pub async fn find_one_with_result() {
let ctx = TestContext::new("mysql://root:@localhost", "find_one_with_result").await;
let bakery = bakery::ActiveModel {
name: Set("SeaSide Bakery".to_owned()),
profit_margin: Set(10.4),
..Default::default()
}
.save(&ctx.db)
.await
.expect("could not insert bakery");
let result = Bakery::find().one(&ctx.db).await.unwrap().unwrap();
assert_eq!(result.id, bakery.id.unwrap());
ctx.delete().await;
}
#[async_std::test]
#[cfg(feature = "sqlx-mysql")]
pub async fn find_by_id_with_no_result() {
let ctx = TestContext::new("mysql://root:@localhost", "find_by_id_with_no_result").await;
let bakery = Bakery::find_by_id(999).one(&ctx.db).await.unwrap();
assert_eq!(bakery, None);
ctx.delete().await;
}
#[async_std::test]
#[cfg(feature = "sqlx-mysql")]
pub async fn find_by_id_with_result() {
let ctx = TestContext::new("mysql://root:@localhost", "find_by_id_with_result").await;
let bakery = bakery::ActiveModel {
name: Set("SeaSide Bakery".to_owned()),
profit_margin: Set(10.4),
..Default::default()
}
.save(&ctx.db)
.await
.expect("could not insert bakery");
let result = Bakery::find_by_id(bakery.id.clone().unwrap())
.one(&ctx.db)
.await
.unwrap()
.unwrap();
assert_eq!(result.id, bakery.id.unwrap());
ctx.delete().await;
}
#[async_std::test]
#[cfg(feature = "sqlx-mysql")]
pub async fn find_all_with_no_result() {
let ctx = TestContext::new("mysql://root:@localhost", "find_all_with_no_result").await;
let bakeries = Bakery::find().all(&ctx.db).await.unwrap();
assert_eq!(bakeries.len(), 0);
ctx.delete().await;
}
#[async_std::test]
#[cfg(feature = "sqlx-mysql")]
pub async fn find_all_with_result() {
let ctx = TestContext::new("mysql://root:@localhost", "find_all_with_result").await;
let _ = bakery::ActiveModel {
name: Set("SeaSide Bakery".to_owned()),
profit_margin: Set(10.4),
..Default::default()
}
.save(&ctx.db)
.await
.expect("could not insert bakery");
let _ = bakery::ActiveModel {
name: Set("Top Bakery".to_owned()),
profit_margin: Set(15.0),
..Default::default()
}
.save(&ctx.db)
.await
.expect("could not insert bakery");
let bakeries = Bakery::find().all(&ctx.db).await.unwrap();
assert_eq!(bakeries.len(), 2);
ctx.delete().await;
}
#[async_std::test]
#[cfg(feature = "sqlx-mysql")]
pub async fn find_all_filter_no_result() {
let ctx = TestContext::new("mysql://root:@localhost", "find_all_filter_no_result").await;
let _ = bakery::ActiveModel {
name: Set("SeaSide Bakery".to_owned()),
profit_margin: Set(10.4),
..Default::default()
}
.save(&ctx.db)
.await
.expect("could not insert bakery");
let _ = bakery::ActiveModel {
name: Set("Top Bakery".to_owned()),
profit_margin: Set(15.0),
..Default::default()
}
.save(&ctx.db)
.await
.expect("could not insert bakery");
let bakeries = Bakery::find()
.filter(bakery::Column::Name.contains("Good"))
.all(&ctx.db)
.await
.unwrap();
assert_eq!(bakeries.len(), 0);
ctx.delete().await;
}
#[async_std::test]
#[cfg(feature = "sqlx-mysql")]
pub async fn find_all_filter_with_results() {
let ctx = TestContext::new("mysql://root:@localhost", "find_all_filter_with_results").await;
let _ = bakery::ActiveModel {
name: Set("SeaSide Bakery".to_owned()),
profit_margin: Set(10.4),
..Default::default()
}
.save(&ctx.db)
.await
.expect("could not insert bakery");
let _ = bakery::ActiveModel {
name: Set("Top Bakery".to_owned()),
profit_margin: Set(15.0),
..Default::default()
}
.save(&ctx.db)
.await
.expect("could not insert bakery");
let bakeries = Bakery::find()
.filter(bakery::Column::Name.contains("bakery"))
.all(&ctx.db)
.await
.unwrap();
assert_eq!(bakeries.len(), 2);
ctx.delete().await;
}

460
tests/relational_tests.rs Normal file
View File

@ -0,0 +1,460 @@
// cargo test --test realtional_tests -- --nocapture
use chrono::offset::Utc;
use rust_decimal::prelude::*;
use rust_decimal_macros::dec;
use sea_orm::{entity::*, query::*, FromQueryResult};
pub mod common;
pub use common::{bakery_chain::*, setup::*, TestContext};
#[async_std::test]
#[cfg(feature = "sqlx-mysql")]
pub async fn left_join() {
let ctx = TestContext::new("mysql://root:@localhost", "test_left_join").await;
let bakery = bakery::ActiveModel {
name: Set("SeaSide Bakery".to_owned()),
profit_margin: Set(10.4),
..Default::default()
}
.save(&ctx.db)
.await
.expect("could not insert bakery");
let _baker_1 = baker::ActiveModel {
name: Set("Baker 1".to_owned()),
contact_details: Set(serde_json::json!({
"mobile": "+61424000000",
"home": "0395555555",
"address": "12 Test St, Testville, Vic, Australia"
})),
bakery_id: Set(Some(bakery.id.clone().unwrap())),
..Default::default()
}
.save(&ctx.db)
.await
.expect("could not insert baker");
let _baker_2 = baker::ActiveModel {
name: Set("Baker 2".to_owned()),
contact_details: Set(serde_json::json!({})),
bakery_id: Set(None),
..Default::default()
}
.save(&ctx.db)
.await
.expect("could not insert baker");
#[derive(Debug, FromQueryResult)]
struct SelectResult {
name: String,
bakery_name: Option<String>,
}
let select = baker::Entity::find()
.left_join(bakery::Entity)
.select_only()
.column(baker::Column::Name)
.column_as(bakery::Column::Name, "bakery_name")
.filter(baker::Column::Name.contains("Baker 1"));
let result = select
.into_model::<SelectResult>()
.one(&ctx.db)
.await
.unwrap()
.unwrap();
assert_eq!(result.bakery_name, Some("SeaSide Bakery".to_string()));
let select = baker::Entity::find()
.left_join(bakery::Entity)
.select_only()
.column(baker::Column::Name)
.column_as(bakery::Column::Name, "bakery_name")
.filter(baker::Column::Name.contains("Baker 2"));
let result = select
.into_model::<SelectResult>()
.one(&ctx.db)
.await
.unwrap()
.unwrap();
assert_eq!(result.bakery_name, None);
ctx.delete().await;
}
#[async_std::test]
#[cfg(feature = "sqlx-mysql")]
pub async fn right_join() {
let ctx = TestContext::new("mysql://root:@localhost", "test_right_join").await;
let bakery = bakery::ActiveModel {
name: Set("SeaSide Bakery".to_owned()),
profit_margin: Set(10.4),
..Default::default()
}
.save(&ctx.db)
.await
.expect("could not insert bakery");
let customer_kate = customer::ActiveModel {
name: Set("Kate".to_owned()),
..Default::default()
}
.save(&ctx.db)
.await
.expect("could not insert customer");
let _customer_jim = customer::ActiveModel {
name: Set("Jim".to_owned()),
..Default::default()
}
.save(&ctx.db)
.await
.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())),
total: Set(dec!(15.10)),
placed_at: Set(Utc::now().naive_utc()),
..Default::default()
}
.save(&ctx.db)
.await
.expect("could not insert order");
#[derive(Debug, FromQueryResult)]
struct SelectResult {
name: String,
order_total: Option<Decimal>,
}
let select = order::Entity::find()
.right_join(customer::Entity)
.select_only()
.column(customer::Column::Name)
.column_as(order::Column::Total, "order_total")
.filter(customer::Column::Name.contains("Kate"));
let result = select
.into_model::<SelectResult>()
.one(&ctx.db)
.await
.unwrap()
.unwrap();
assert_eq!(result.order_total, Some(dec!(15.10)));
let select = order::Entity::find()
.right_join(customer::Entity)
.select_only()
.column(customer::Column::Name)
.column_as(order::Column::Total, "order_total")
.filter(customer::Column::Name.contains("Jim"));
let result = select
.into_model::<SelectResult>()
.one(&ctx.db)
.await
.unwrap()
.unwrap();
assert_eq!(result.order_total, None);
ctx.delete().await;
}
#[async_std::test]
#[cfg(feature = "sqlx-mysql")]
pub async fn inner_join() {
let ctx = TestContext::new("mysql://root:@localhost", "test_inner_join").await;
let bakery = bakery::ActiveModel {
name: Set("SeaSide Bakery".to_owned()),
profit_margin: Set(10.4),
..Default::default()
}
.save(&ctx.db)
.await
.expect("could not insert bakery");
let customer_kate = customer::ActiveModel {
name: Set("Kate".to_owned()),
..Default::default()
}
.save(&ctx.db)
.await
.expect("could not insert customer");
let _customer_jim = customer::ActiveModel {
name: Set("Jim".to_owned()),
..Default::default()
}
.save(&ctx.db)
.await
.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())),
total: Set(dec!(15.10)),
placed_at: Set(Utc::now().naive_utc()),
..Default::default()
}
.save(&ctx.db)
.await
.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())),
total: Set(dec!(100.00)),
placed_at: Set(Utc::now().naive_utc()),
..Default::default()
}
.save(&ctx.db)
.await
.expect("could not insert order");
#[derive(Debug, FromQueryResult)]
struct SelectResult {
name: String,
order_total: Option<Decimal>,
}
let select = order::Entity::find()
.inner_join(customer::Entity)
.select_only()
.column(customer::Column::Name)
.column_as(order::Column::Total, "order_total");
let results = select
.into_model::<SelectResult>()
.all(&ctx.db)
.await
.unwrap();
assert_eq!(results.len(), 2);
assert!((&results)
.into_iter()
.any(|result| result.name == customer_kate.name.clone().unwrap()
&& result.order_total == Some(kate_order_1.total.clone().unwrap())));
assert!((&results)
.into_iter()
.any(|result| result.name == customer_kate.name.clone().unwrap()
&& result.order_total == Some(kate_order_2.total.clone().unwrap())));
ctx.delete().await;
}
#[async_std::test]
#[cfg(feature = "sqlx-mysql")]
pub async fn group_by() {
let ctx = TestContext::new("mysql://root:@localhost", "test_group_by").await;
let bakery = bakery::ActiveModel {
name: Set("SeaSide Bakery".to_owned()),
profit_margin: Set(10.4),
..Default::default()
}
.save(&ctx.db)
.await
.expect("could not insert bakery");
let customer_kate = customer::ActiveModel {
name: Set("Kate".to_owned()),
..Default::default()
}
.save(&ctx.db)
.await
.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())),
total: Set(dec!(99.95)),
placed_at: Set(Utc::now().naive_utc()),
..Default::default()
}
.save(&ctx.db)
.await
.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())),
total: Set(dec!(200.00)),
placed_at: Set(Utc::now().naive_utc()),
..Default::default()
}
.save(&ctx.db)
.await
.expect("could not insert order");
#[derive(Debug, FromQueryResult)]
struct SelectResult {
name: String,
number_orders: Option<i32>,
total_spent: Option<Decimal>,
min_spent: Option<Decimal>,
max_spent: Option<Decimal>,
}
let select = customer::Entity::find()
.left_join(order::Entity)
.select_only()
.column(customer::Column::Name)
.column_as(order::Column::Total.count(), "number_orders")
.column_as(order::Column::Total.sum(), "total_spent")
.column_as(order::Column::Total.min(), "min_spent")
.column_as(order::Column::Total.max(), "max_spent")
.group_by(customer::Column::Name);
let result = select
.into_model::<SelectResult>()
.one(&ctx.db)
.await
.unwrap()
.unwrap();
assert_eq!(result.number_orders, Some(2));
assert_eq!(
result.total_spent,
Some(kate_order_1.total.clone().unwrap() + kate_order_2.total.clone().unwrap())
);
assert_eq!(
result.min_spent,
Some(
kate_order_1
.total
.clone()
.unwrap()
.min(kate_order_2.total.clone().unwrap())
)
);
assert_eq!(
result.max_spent,
Some(
kate_order_1
.total
.clone()
.unwrap()
.max(kate_order_2.total.clone().unwrap())
)
);
ctx.delete().await;
}
#[async_std::test]
#[cfg(feature = "sqlx-mysql")]
pub async fn having() {
// customers with orders with total equal to $90
let ctx = TestContext::new("mysql://root:@localhost", "test_having").await;
let bakery = bakery::ActiveModel {
name: Set("SeaSide Bakery".to_owned()),
profit_margin: Set(10.4),
..Default::default()
}
.save(&ctx.db)
.await
.expect("could not insert bakery");
let customer_kate = customer::ActiveModel {
name: Set("Kate".to_owned()),
..Default::default()
}
.save(&ctx.db)
.await
.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())),
total: Set(dec!(100.00)),
placed_at: Set(Utc::now().naive_utc()),
..Default::default()
}
.save(&ctx.db)
.await
.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())),
total: Set(dec!(12.00)),
placed_at: Set(Utc::now().naive_utc()),
..Default::default()
}
.save(&ctx.db)
.await
.expect("could not insert order");
let customer_bob = customer::ActiveModel {
name: Set("Bob".to_owned()),
..Default::default()
}
.save(&ctx.db)
.await
.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())),
total: Set(dec!(50.0)),
placed_at: Set(Utc::now().naive_utc()),
..Default::default()
}
.save(&ctx.db)
.await
.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())),
total: Set(dec!(50.0)),
placed_at: Set(Utc::now().naive_utc()),
..Default::default()
}
.save(&ctx.db)
.await
.expect("could not insert order");
#[derive(Debug, FromQueryResult)]
struct SelectResult {
name: String,
order_total: Option<Decimal>,
}
let results = customer::Entity::find()
.inner_join(order::Entity)
.select_only()
.column(customer::Column::Name)
.column_as(order::Column::Total, "order_total")
.group_by(customer::Column::Name)
.group_by(order::Column::Total)
.having(order::Column::Total.gt(dec!(90.00)))
.into_model::<SelectResult>()
.all(&ctx.db)
.await
.unwrap();
assert_eq!(results.len(), 1);
assert_eq!(results[0].name, customer_kate.name.clone().unwrap());
assert_eq!(
results[0].order_total,
Some(kate_order_1.total.clone().unwrap())
);
ctx.delete().await;
}

View File

@ -0,0 +1,269 @@
use chrono::offset::Utc;
use rust_decimal::prelude::*;
use rust_decimal_macros::dec;
use sea_orm::{entity::*, query::*, DatabaseConnection, FromQueryResult};
use uuid::Uuid;
pub mod common;
pub use common::{bakery_chain::*, setup::*, TestContext};
#[async_std::test]
#[cfg(feature = "sqlx-mysql")]
pub async fn test_multiple_operations() {
let ctx = TestContext::new("mysql://root:@localhost", "multiple_sequential_operations").await;
init_setup(&ctx.db).await;
let baker_least_sales = find_baker_least_sales(&ctx.db).await.unwrap();
assert_eq!(baker_least_sales.name, "Baker 2");
let new_cake = create_cake(&ctx.db, baker_least_sales).await.unwrap();
create_order(&ctx.db, new_cake).await;
let baker_least_sales = find_baker_least_sales(&ctx.db).await.unwrap();
assert_eq!(baker_least_sales.name, "Baker 1");
}
async fn init_setup(db: &DatabaseConnection) {
let bakery = bakery::ActiveModel {
name: Set("SeaSide Bakery".to_owned()),
profit_margin: Set(10.4),
..Default::default()
}
.save(db)
.await
.expect("could not insert bakery");
let baker_1 = baker::ActiveModel {
name: Set("Baker 1".to_owned()),
contact_details: Set(serde_json::json!({})),
bakery_id: Set(Some(bakery.id.clone().unwrap())),
..Default::default()
}
.save(db)
.await
.expect("could not insert baker");
let _baker_2 = baker::ActiveModel {
name: Set("Baker 2".to_owned()),
contact_details: Set(serde_json::json!({})),
bakery_id: Set(Some(bakery.id.clone().unwrap())),
..Default::default()
}
.save(db)
.await
.expect("could not insert baker");
let mud_cake = cake::ActiveModel {
name: Set("Mud Cake".to_owned()),
price: Set(dec!(10.25)),
gluten_free: Set(false),
serial: Set(Uuid::new_v4()),
bakery_id: Set(Some(bakery.id.clone().unwrap())),
..Default::default()
};
let cake_insert_res: InsertResult = Cake::insert(mud_cake)
.exec(db)
.await
.expect("could not insert cake");
let cake_baker = cakes_bakers::ActiveModel {
cake_id: Set(cake_insert_res.last_insert_id as i32),
baker_id: Set(baker_1.id.clone().unwrap()),
..Default::default()
};
let _cake_baker_res: InsertResult = CakesBakers::insert(cake_baker)
.exec(db)
.await
.expect("could not insert cake_baker");
let customer_kate = customer::ActiveModel {
name: Set("Kate".to_owned()),
..Default::default()
}
.save(db)
.await
.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())),
total: Set(dec!(99.95)),
placed_at: Set(Utc::now().naive_utc()),
..Default::default()
}
.save(db)
.await
.expect("could not insert order");
let _lineitem = lineitem::ActiveModel {
cake_id: Set(Some(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())),
..Default::default()
}
.save(db)
.await
.expect("could not insert order");
let _lineitem2 = lineitem::ActiveModel {
cake_id: Set(Some(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())),
..Default::default()
}
.save(db)
.await
.expect("could not insert order");
}
async fn find_baker_least_sales(db: &DatabaseConnection) -> Option<baker::Model> {
#[derive(Debug, FromQueryResult)]
struct SelectResult {
id: i32,
cakes_sold_opt: Option<Decimal>,
}
#[derive(Debug)]
struct LeastSalesBakerResult {
id: i32,
cakes_sold: Decimal,
}
let rel: RelationDef = cakes_bakers::Entity::belongs_to(baker::Entity)
.from(cakes_bakers::Column::BakerId)
.to(baker::Column::Id)
.into();
let rel2: RelationDef = cakes_bakers::Entity::belongs_to(cake::Entity)
.from(cakes_bakers::Column::CakeId)
.to(cake::Column::Id)
.into();
let rel3: RelationDef = cake::Entity::has_many(lineitem::Entity)
.from(cake::Column::Id)
.to(lineitem::Column::CakeId)
.into();
let select = cakes_bakers::Entity::find()
.join(JoinType::RightJoin, rel)
.join(JoinType::LeftJoin, rel2)
.join(JoinType::LeftJoin, rel3)
.select_only()
.column(baker::Column::Id)
.column_as(lineitem::Column::Quantity.sum(), "cakes_sold_opt")
.group_by(baker::Column::Id);
let mut results: Vec<LeastSalesBakerResult> = select
.into_model::<SelectResult>()
.all(&db)
.await
.unwrap()
.into_iter()
.map(|b| LeastSalesBakerResult {
id: b.id.clone(),
cakes_sold: b.cakes_sold_opt.unwrap_or(dec!(0)),
})
.collect();
results.sort_by(|a, b| b.cakes_sold.cmp(&a.cakes_sold));
Baker::find_by_id(results.last().unwrap().id)
.one(db)
.await
.unwrap()
}
async fn create_cake(db: &DatabaseConnection, baker: baker::Model) -> Option<cake::Model> {
let new_cake = cake::ActiveModel {
name: Set("New Cake".to_owned()),
price: Set(dec!(8.00)),
gluten_free: Set(false),
serial: Set(Uuid::new_v4()),
bakery_id: Set(Some(baker.bakery_id.clone().unwrap())),
..Default::default()
};
let cake_insert_res: InsertResult = Cake::insert(new_cake)
.exec(db)
.await
.expect("could not insert cake");
let cake_baker = cakes_bakers::ActiveModel {
cake_id: Set(cake_insert_res.last_insert_id as i32),
baker_id: Set(baker.id),
..Default::default()
};
let _cake_baker_res: InsertResult = CakesBakers::insert(cake_baker)
.exec(db)
.await
.expect("could not insert cake_baker");
Cake::find_by_id(cake_insert_res.last_insert_id)
.one(db)
.await
.unwrap()
}
async fn create_order(db: &DatabaseConnection, cake: cake::Model) {
let another_customer = customer::ActiveModel {
name: Set("John".to_owned()),
..Default::default()
}
.save(db)
.await
.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())),
total: Set(dec!(200.00)),
placed_at: Set(Utc::now().naive_utc()),
..Default::default()
}
.save(db)
.await
.expect("could not insert order");
let _lineitem = lineitem::ActiveModel {
cake_id: Set(Some(cake.id)),
price: Set(dec!(10.00)),
quantity: Set(300),
order_id: Set(Some(order.id.clone().unwrap())),
..Default::default()
}
.save(db)
.await
.expect("could not insert order");
}
pub async fn test_delete_bakery(db: &DatabaseConnection) {
let initial_bakeries = Bakery::find().all(db).await.unwrap().len();
let bakery = bakery::ActiveModel {
name: Set("SeaSide Bakery".to_owned()),
profit_margin: Set(10.4),
..Default::default()
}
.save(db)
.await
.expect("could not insert bakery");
assert_eq!(
Bakery::find().all(db).await.unwrap().len(),
initial_bakeries + 1
);
let _result = bakery.delete(db).await.expect("failed to delete bakery");
assert_eq!(
Bakery::find().all(db).await.unwrap().len(),
initial_bakeries
);
}