From ba77fb80aa9e7f6bdba85f2007cd61f7bef3cb71 Mon Sep 17 00:00:00 2001 From: Sam Samai Date: Sun, 11 Jul 2021 22:08:55 +1000 Subject: [PATCH] Create TestContext for setting up and tearing down the database after each test --- tests/common/bakery_chain/baker.rs | 81 +++++++ tests/common/bakery_chain/bakery.rs | 84 +++++++ .../bakery_chain/bakery_chain_erd.drawio | 1 + .../common/bakery_chain/bakery_chain_erd.jpg | Bin 0 -> 50804 bytes tests/common/bakery_chain/cake.rs | 96 ++++++++ tests/common/bakery_chain/cakes_bakers.rs | 68 ++++++ tests/common/bakery_chain/customer.rs | 68 ++++++ tests/common/bakery_chain/lineitem.rs | 89 ++++++++ tests/common/bakery_chain/mod.rs | 15 ++ tests/common/bakery_chain/order.rs | 97 ++++++++ tests/common/mod.rs | 50 +++++ tests/common/schema.rs | 211 ++++++++++++++++++ tests/relational_tests.rs | 37 +++ 13 files changed, 897 insertions(+) create mode 100644 tests/common/bakery_chain/baker.rs create mode 100644 tests/common/bakery_chain/bakery.rs create mode 100644 tests/common/bakery_chain/bakery_chain_erd.drawio create mode 100644 tests/common/bakery_chain/bakery_chain_erd.jpg create mode 100644 tests/common/bakery_chain/cake.rs create mode 100644 tests/common/bakery_chain/cakes_bakers.rs create mode 100644 tests/common/bakery_chain/customer.rs create mode 100644 tests/common/bakery_chain/lineitem.rs create mode 100644 tests/common/bakery_chain/mod.rs create mode 100644 tests/common/bakery_chain/order.rs create mode 100644 tests/common/mod.rs create mode 100644 tests/common/schema.rs create mode 100644 tests/relational_tests.rs diff --git a/tests/common/bakery_chain/baker.rs b/tests/common/bakery_chain/baker.rs new file mode 100644 index 00000000..b359e7ef --- /dev/null +++ b/tests/common/bakery_chain/baker.rs @@ -0,0 +1,81 @@ +use sea_orm::entity::prelude::*; + +#[derive(Copy, Clone, Default, Debug, DeriveEntity)] +pub struct Entity; + +impl EntityName for Entity { + fn table_name(&self) -> &str { + "baker" + } +} + +#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)] +pub struct Model { + pub id: i32, + pub name: String, + pub bakery_id: Option, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] +pub enum Column { + Id, + Name, + BakeryId, +} + +#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)] +pub enum PrimaryKey { + Id, +} + +impl PrimaryKeyTrait for PrimaryKey { + fn auto_increment() -> bool { + true + } +} + +#[derive(Copy, Clone, Debug, EnumIter)] +pub enum Relation { + Bakery, +} + +impl ColumnTrait for Column { + type EntityName = Entity; + + fn def(&self) -> ColumnDef { + match self { + Self::Id => ColumnType::Integer.def(), + Self::Name => ColumnType::String(None).def(), + Self::BakeryId => ColumnType::Integer.def(), + } + } +} + +impl RelationTrait for Relation { + fn def(&self) -> RelationDef { + match self { + Self::Bakery => Entity::belongs_to(super::bakery::Entity) + .from(Column::BakeryId) + .to(super::bakery::Column::Id) + .into(), + } + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Bakery.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + super::cakes_bakers::Relation::Cake.def() + } + + fn via() -> Option { + Some(super::cakes_bakers::Relation::Baker.def().rev()) + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/tests/common/bakery_chain/bakery.rs b/tests/common/bakery_chain/bakery.rs new file mode 100644 index 00000000..61803329 --- /dev/null +++ b/tests/common/bakery_chain/bakery.rs @@ -0,0 +1,84 @@ +use sea_orm::entity::prelude::*; + +#[derive(Copy, Clone, Default, Debug, DeriveEntity)] +pub struct Entity; + +impl EntityName for Entity { + fn table_name(&self) -> &str { + "bakery" + } +} + +#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)] +pub struct Model { + pub id: i32, + pub name: String, + pub profit_margin: f64, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] +pub enum Column { + Id, + Name, + ProfitMargin, +} + +#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)] +pub enum PrimaryKey { + Id, +} + +impl PrimaryKeyTrait for PrimaryKey { + fn auto_increment() -> bool { + true + } +} + +#[derive(Copy, Clone, Debug, EnumIter)] +pub enum Relation { + Baker, + Order, + Cake, +} + +impl ColumnTrait for Column { + type EntityName = Entity; + + fn def(&self) -> ColumnDef { + match self { + Self::Id => ColumnType::Integer.def(), + Self::Name => ColumnType::String(None).def(), + Self::ProfitMargin => ColumnType::Float.def(), + } + } +} + +impl RelationTrait for Relation { + fn def(&self) -> RelationDef { + match self { + Self::Baker => Entity::has_many(super::baker::Entity).into(), + Self::Order => Entity::has_many(super::order::Entity).into(), + Self::Cake => Entity::has_many(super::cake::Entity).into(), + } + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Baker.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Order.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Cake.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/tests/common/bakery_chain/bakery_chain_erd.drawio b/tests/common/bakery_chain/bakery_chain_erd.drawio new file mode 100644 index 00000000..10084d0a --- /dev/null +++ b/tests/common/bakery_chain/bakery_chain_erd.drawio @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/tests/common/bakery_chain/bakery_chain_erd.jpg b/tests/common/bakery_chain/bakery_chain_erd.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f44da9aa26859510a2b04cbe530ffe06bb409899 GIT binary patch literal 50804 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 Dc!gCm literal 0 HcmV?d00001 diff --git a/tests/common/bakery_chain/cake.rs b/tests/common/bakery_chain/cake.rs new file mode 100644 index 00000000..1e134de5 --- /dev/null +++ b/tests/common/bakery_chain/cake.rs @@ -0,0 +1,96 @@ +use rust_decimal::prelude::*; +use sea_orm::entity::prelude::*; + +#[derive(Copy, Clone, Default, Debug, DeriveEntity)] +pub struct Entity; + +impl EntityName for Entity { + fn table_name(&self) -> &str { + "cake" + } +} + +#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)] +pub struct Model { + pub id: i32, + pub name: String, + pub price: Decimal, + pub bakery_id: Option, + pub gluten_free: bool, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] +pub enum Column { + Id, + Name, + Price, + BakeryId, + GlutenFree, +} + +#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)] +pub enum PrimaryKey { + Id, +} + +impl PrimaryKeyTrait for PrimaryKey { + fn auto_increment() -> bool { + true + } +} + +#[derive(Copy, Clone, Debug, EnumIter)] +pub enum Relation { + Bakery, + Lineitem, +} + +impl ColumnTrait for Column { + type EntityName = Entity; + + fn def(&self) -> ColumnDef { + match self { + Self::Id => ColumnType::Integer.def(), + Self::Name => ColumnType::String(None).def(), + Self::Price => ColumnType::Decimal(Some((19, 4))).def(), + Self::BakeryId => ColumnType::Integer.def(), + Self::GlutenFree => ColumnType::Boolean.def(), + } + } +} + +impl RelationTrait for Relation { + fn def(&self) -> RelationDef { + match self { + Self::Bakery => Entity::belongs_to(super::bakery::Entity) + .from(Column::BakeryId) + .to(super::bakery::Column::Id) + .into(), + Self::Lineitem => Entity::has_many(super::lineitem::Entity).into(), + } + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Bakery.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + super::cakes_bakers::Relation::Baker.def() + } + + fn via() -> Option { + Some(super::cakes_bakers::Relation::Cake.def().rev()) + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Lineitem.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/tests/common/bakery_chain/cakes_bakers.rs b/tests/common/bakery_chain/cakes_bakers.rs new file mode 100644 index 00000000..8106bbdf --- /dev/null +++ b/tests/common/bakery_chain/cakes_bakers.rs @@ -0,0 +1,68 @@ +use sea_orm::entity::prelude::*; + +#[derive(Copy, Clone, Default, Debug, DeriveEntity)] +pub struct Entity; + +impl EntityName for Entity { + fn table_name(&self) -> &str { + "cakes_bakers" + } +} + +#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)] +pub struct Model { + pub cake_id: i32, + pub baker_id: i32, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] +pub enum Column { + CakeId, + BakerId, +} + +#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)] +pub enum PrimaryKey { + CakeId, + BakerId, +} + +impl PrimaryKeyTrait for PrimaryKey { + fn auto_increment() -> bool { + false + } +} + +#[derive(Copy, Clone, Debug, EnumIter)] +pub enum Relation { + Cake, + Baker, +} + +impl ColumnTrait for Column { + type EntityName = Entity; + + fn def(&self) -> ColumnDef { + match self { + Self::CakeId => ColumnType::Integer.def(), + Self::BakerId => ColumnType::Integer.def(), + } + } +} + +impl RelationTrait for Relation { + fn def(&self) -> RelationDef { + match self { + Self::Cake => Entity::belongs_to(super::cake::Entity) + .from(Column::CakeId) + .to(super::cake::Column::Id) + .into(), + Self::Baker => Entity::belongs_to(super::baker::Entity) + .from(Column::BakerId) + .to(super::baker::Column::Id) + .into(), + } + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/tests/common/bakery_chain/customer.rs b/tests/common/bakery_chain/customer.rs new file mode 100644 index 00000000..a7464082 --- /dev/null +++ b/tests/common/bakery_chain/customer.rs @@ -0,0 +1,68 @@ +use sea_orm::entity::prelude::*; + +#[derive(Copy, Clone, Default, Debug, DeriveEntity)] +pub struct Entity; + +impl EntityName for Entity { + fn table_name(&self) -> &str { + "customer" + } +} + +#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)] +pub struct Model { + pub id: i32, + pub name: String, + pub notes: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] +pub enum Column { + Id, + Name, + Notes, +} + +#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)] +pub enum PrimaryKey { + Id, +} + +impl PrimaryKeyTrait for PrimaryKey { + fn auto_increment() -> bool { + true + } +} + +#[derive(Copy, Clone, Debug, EnumIter)] +pub enum Relation { + Order, +} + +impl ColumnTrait for Column { + type EntityName = Entity; + + fn def(&self) -> ColumnDef { + match self { + Self::Id => ColumnType::Integer.def(), + Self::Name => ColumnType::String(None).def(), + Self::Notes => ColumnType::Text.def(), + } + } +} + +impl RelationTrait for Relation { + fn def(&self) -> RelationDef { + match self { + Self::Order => Entity::has_many(super::order::Entity).into(), + } + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Order.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/tests/common/bakery_chain/lineitem.rs b/tests/common/bakery_chain/lineitem.rs new file mode 100644 index 00000000..94b15cac --- /dev/null +++ b/tests/common/bakery_chain/lineitem.rs @@ -0,0 +1,89 @@ +use rust_decimal::prelude::*; +use sea_orm::entity::prelude::*; + +#[derive(Copy, Clone, Default, Debug, DeriveEntity)] +pub struct Entity; + +impl EntityName for Entity { + fn table_name(&self) -> &str { + "lineitem" + } +} + +#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)] +pub struct Model { + pub id: i32, + pub price: Decimal, + pub quantity: i32, + pub order_id: Option, + pub cake_id: Option, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] +pub enum Column { + Id, + Price, + Quantity, + OrderId, + CakeId, +} + +#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)] +pub enum PrimaryKey { + Id, +} + +impl PrimaryKeyTrait for PrimaryKey { + fn auto_increment() -> bool { + true + } +} + +#[derive(Copy, Clone, Debug, EnumIter)] +pub enum Relation { + Order, + Cake, +} + +impl ColumnTrait for Column { + type EntityName = Entity; + + fn def(&self) -> ColumnDef { + match self { + Self::Id => ColumnType::Integer.def(), + Self::Price => ColumnType::Money(Some((19, 4))).def(), + Self::Quantity => ColumnType::Integer.def(), + Self::OrderId => ColumnType::Integer.def(), + Self::CakeId => ColumnType::Integer.def(), + } + } +} + +impl RelationTrait for Relation { + fn def(&self) -> RelationDef { + match self { + Self::Order => Entity::belongs_to(super::order::Entity) + .from(Column::OrderId) + .to(super::order::Column::Id) + .into(), + Self::Cake => Entity::belongs_to(super::cake::Entity) + .from(Column::CakeId) + .to(super::cake::Column::Id) + .into(), + } + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Order.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Cake.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/tests/common/bakery_chain/mod.rs b/tests/common/bakery_chain/mod.rs new file mode 100644 index 00000000..89028aab --- /dev/null +++ b/tests/common/bakery_chain/mod.rs @@ -0,0 +1,15 @@ +pub mod baker; +pub mod bakery; +pub mod cake; +pub mod cakes_bakers; +pub mod customer; +pub mod lineitem; +pub mod order; + +pub use super::baker::Entity as Baker; +pub use super::bakery::Entity as Bakery; +pub use super::cake::Entity as Cake; +pub use super::cakes_bakers::Entity as CakesBakers; +pub use super::customer::Entity as Customer; +pub use super::lineitem::Entity as Lineitem; +pub use super::order::Entity as Order; diff --git a/tests/common/bakery_chain/order.rs b/tests/common/bakery_chain/order.rs new file mode 100644 index 00000000..de38df60 --- /dev/null +++ b/tests/common/bakery_chain/order.rs @@ -0,0 +1,97 @@ +use rust_decimal::prelude::*; +use sea_orm::entity::prelude::*; + +#[derive(Copy, Clone, Default, Debug, DeriveEntity)] +pub struct Entity; + +impl EntityName for Entity { + fn table_name(&self) -> &str { + "order" + } +} + +#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)] +pub struct Model { + pub id: i32, + pub total: Decimal, + pub bakery_id: Option, + pub customer_id: Option, + pub placed_at: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] +pub enum Column { + Id, + Total, + BakeryId, + CustomerId, + PlacedAt, +} + +#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)] +pub enum PrimaryKey { + Id, +} + +impl PrimaryKeyTrait for PrimaryKey { + fn auto_increment() -> bool { + true + } +} + +#[derive(Copy, Clone, Debug, EnumIter)] +pub enum Relation { + Bakery, + Customer, + Lineitem, +} + +impl ColumnTrait for Column { + type EntityName = Entity; + + fn def(&self) -> ColumnDef { + match self { + Self::Id => ColumnType::Integer.def(), + Self::Total => ColumnType::Decimal(Some((19, 4))).def(), + Self::BakeryId => ColumnType::Integer.def(), + Self::CustomerId => ColumnType::Integer.def(), + Self::PlacedAt => ColumnType::DateTime.def(), + } + } +} + +impl RelationTrait for Relation { + fn def(&self) -> RelationDef { + match self { + Self::Bakery => Entity::belongs_to(super::bakery::Entity) + .from(Column::BakeryId) + .to(super::bakery::Column::Id) + .into(), + Self::Customer => Entity::belongs_to(super::customer::Entity) + .from(Column::CustomerId) + .to(super::customer::Column::Id) + .into(), + Self::Lineitem => Entity::has_many(super::lineitem::Entity).into(), + } + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Bakery.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Customer.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Lineitem.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/tests/common/mod.rs b/tests/common/mod.rs new file mode 100644 index 00000000..c891b51a --- /dev/null +++ b/tests/common/mod.rs @@ -0,0 +1,50 @@ +pub mod schema; +use sea_orm::{Database, DatabaseConnection}; +pub mod bakery_chain; +pub use bakery_chain::*; + +macro_rules! function { + () => {{ + fn f() {} + fn type_name_of(_: T) -> &'static str { + std::any::type_name::() + } + let name = type_name_of(f); + &name[..name.len() - 3] + }}; +} + +pub struct TestContext { + base_url: String, + db_name: String, + pub db_conn: DatabaseConnection, +} + +impl TestContext { + pub async fn new(base_url: &str, db_name: &str) -> Self { + let db_conn = Database::connect("sqlite::memory:").await.unwrap(); + Self::setup_schema(&db_conn).await; + + Self { + base_url: base_url.to_string(), + db_name: db_name.to_string(), + db_conn, + } + } + + async fn setup_schema(db: &DatabaseConnection) { + 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()); + } +} + +impl Drop for TestContext { + fn drop(&mut self) { + println!("dropping context"); + } +} diff --git a/tests/common/schema.rs b/tests/common/schema.rs new file mode 100644 index 00000000..6ebcc381 --- /dev/null +++ b/tests/common/schema.rs @@ -0,0 +1,211 @@ +use sea_orm::{error::*, sea_query, DbConn, ExecResult}; +use sea_query::{ColumnDef, ForeignKey, ForeignKeyAction, Index, TableCreateStatement}; + +pub use super::bakery_chain::*; + +async fn create_table(db: &DbConn, stmt: &TableCreateStatement) -> Result { + let builder = db.get_schema_builder_backend(); + db.execute(builder.build(stmt)).await +} + +pub async fn create_bakery_table(db: &DbConn) -> Result { + let stmt = sea_query::Table::create() + .table(bakery::Entity) + .if_not_exists() + .col( + ColumnDef::new(bakery::Column::Id) + .integer() + .not_null() + .auto_increment() + .primary_key(), + ) + .col(ColumnDef::new(bakery::Column::Name).string()) + .col(ColumnDef::new(bakery::Column::ProfitMargin).float()) + .to_owned(); + + create_table(db, &stmt).await +} + +pub async fn create_baker_table(db: &DbConn) -> Result { + let stmt = sea_query::Table::create() + .table(baker::Entity) + .if_not_exists() + .col( + ColumnDef::new(baker::Column::Id) + .integer() + .not_null() + .auto_increment() + .primary_key(), + ) + .col(ColumnDef::new(baker::Column::Name).string()) + .col(ColumnDef::new(baker::Column::BakeryId).integer()) + .foreign_key( + ForeignKey::create() + .name("FK_baker_bakery") + .from(baker::Entity, baker::Column::BakeryId) + .to(bakery::Entity, bakery::Column::Id) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), + ) + .to_owned(); + + create_table(db, &stmt).await +} + +pub async fn create_customer_table(db: &DbConn) -> Result { + let stmt = sea_query::Table::create() + .table(customer::Entity) + .if_not_exists() + .col( + ColumnDef::new(customer::Column::Id) + .integer() + .not_null() + .auto_increment() + .primary_key(), + ) + .col(ColumnDef::new(customer::Column::Name).string()) + .col(ColumnDef::new(customer::Column::Notes).text()) + .to_owned(); + + create_table(db, &stmt).await +} + +pub async fn create_order_table(db: &DbConn) -> Result { + let stmt = sea_query::Table::create() + .table(order::Entity) + .if_not_exists() + .col( + ColumnDef::new(order::Column::Id) + .integer() + .not_null() + .auto_increment() + .primary_key(), + ) + .col(ColumnDef::new(order::Column::Total).float()) + .col(ColumnDef::new(order::Column::BakeryId).integer().not_null()) + .col( + ColumnDef::new(order::Column::CustomerId) + .integer() + .not_null(), + ) + .col( + ColumnDef::new(order::Column::PlacedAt) + .date_time() + .not_null(), + ) + .foreign_key( + ForeignKey::create() + .name("FK_order_bakery") + .from(order::Entity, order::Column::BakeryId) + .to(bakery::Entity, bakery::Column::Id) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), + ) + .foreign_key( + ForeignKey::create() + .name("FK_order_customer") + .from(order::Entity, order::Column::CustomerId) + .to(customer::Entity, customer::Column::Id) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), + ) + .to_owned(); + + create_table(db, &stmt).await +} + +pub async fn create_lineitem_table(db: &DbConn) -> Result { + let stmt = sea_query::Table::create() + .table(lineitem::Entity) + .if_not_exists() + .col( + ColumnDef::new(lineitem::Column::Id) + .integer() + .not_null() + .auto_increment() + .primary_key(), + ) + .col(ColumnDef::new(lineitem::Column::Price).decimal()) + .col(ColumnDef::new(lineitem::Column::Quantity).integer()) + .col( + ColumnDef::new(lineitem::Column::OrderId) + .integer() + .not_null(), + ) + .col( + ColumnDef::new(lineitem::Column::CakeId) + .integer() + .not_null(), + ) + .foreign_key( + ForeignKey::create() + .name("FK_lineitem_order") + .from(lineitem::Entity, lineitem::Column::OrderId) + .to(order::Entity, order::Column::Id) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), + ) + .foreign_key( + ForeignKey::create() + .name("FK_lineitem_cake") + .from(lineitem::Entity, lineitem::Column::CakeId) + .to(cake::Entity, cake::Column::Id) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), + ) + .to_owned(); + + create_table(db, &stmt).await +} + +pub async fn create_cakes_bakers_table(db: &DbConn) -> Result { + let stmt = sea_query::Table::create() + .table(cakes_bakers::Entity) + .if_not_exists() + .col( + ColumnDef::new(cakes_bakers::Column::CakeId) + .integer() + .not_null(), + ) + .col( + ColumnDef::new(cakes_bakers::Column::BakerId) + .integer() + .not_null(), + ) + .primary_key( + Index::create() + .col(cakes_bakers::Column::CakeId) + .col(cakes_bakers::Column::BakerId), + ) + .to_owned(); + + create_table(db, &stmt).await +} + +pub async fn create_cake_table(db: &DbConn) -> Result { + let stmt = sea_query::Table::create() + .table(cake::Entity) + .if_not_exists() + .col( + ColumnDef::new(cake::Column::Id) + .integer() + .not_null() + .auto_increment() + .primary_key(), + ) + .col(ColumnDef::new(cake::Column::Name).string()) + .col(ColumnDef::new(cake::Column::Price).float()) + .col(ColumnDef::new(cake::Column::BakeryId).integer().not_null()) + .foreign_key( + ForeignKey::create() + .name("FK_cake_bakery") + .from(cake::Entity, cake::Column::BakeryId) + .to(bakery::Entity, bakery::Column::Id) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), + ) + .col(ColumnDef::new(cake::Column::GlutenFree).boolean()) + .to_owned(); + + create_table(db, &stmt).await +} diff --git a/tests/relational_tests.rs b/tests/relational_tests.rs new file mode 100644 index 00000000..edefa9ef --- /dev/null +++ b/tests/relational_tests.rs @@ -0,0 +1,37 @@ +use sea_orm::{entity::*, InsertResult}; + +pub mod bakery_chain; +pub use bakery_chain::*; + +pub mod common; +pub use common::TestContext; + +#[async_std::test] +// cargo test --test realtional_tests -- --nocapture +async fn main() { + test_left_join().await; +} + +pub async fn test_left_join() { + let ctx = TestContext::new("test", "test").await; + + let seaside_bakery = bakery::ActiveModel { + name: Set("SeaSide Bakery".to_owned()), + profit_margin: Set(10.4), + ..Default::default() + }; + let res: InsertResult = Bakery::insert(seaside_bakery) + .exec(&ctx.db_conn) + .await + .expect("could not insert bakery"); + + let bakery: Option = Bakery::find_by_id(res.last_insert_id) + .one(&ctx.db_conn) + .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); +}