Test cases improvement (#1811)

* adds find_with_linked test

* WIP(related test)

* mock related test done

* complete relation test

* loader update

* find_with/also_related missing test case for empty from other side

* comments fixup

* revert loader test

* related select test done

* find with/also linked test cases

* removed due to it being functionally same as the new one

* fmt, remove excess import

* improved model generation

* issue related test case #1790

* added loader test cases and slight improvement to find_related/linked

* miscellaneous changes

* added empty insert, merge load_one test case

* completed loader many to many test case, fmt

* removed empty_insert test case for now

* commented insert_test

* added Cargo.toml for issue 1790's folder

* buffed salvo version for ci(0.49 yanked)

* revert version for salvo example
This commit is contained in:
Ivan Yiu 2023-08-18 20:02:17 +08:00 committed by GitHub
parent 2a8efed98d
commit d50312c081
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 1251 additions and 61 deletions

18
issues/1790/Cargo.toml Normal file
View File

@ -0,0 +1,18 @@
[workspace]
# A separate workspace
[package]
name = "sea-orm-issues-1790"
version = "0.1.0"
edition = "2023"
publish = false
[dependencies]
anyhow = "1"
serde = "1"
tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros"] }
[dependencies.sea-orm]
path = "../../"
default-features = false
features = ["macros", "runtime-tokio-native-tls", "sqlx-sqlite"]

View File

@ -0,0 +1,58 @@
mod tests {
// currently ok
#[test]
fn insert_do_nothing_postgres() {
assert_eq!(
Insert::<cake::ActiveModel>::new()
.add(cake::Model {
id: 1,
name: "Apple Pie".to_owned(),
})
.on_conflict(OnConflict::new()
.do_nothing()
.to_owned()
)
.build(DbBackend::Postgres)
.to_string(),
r#"INSERT INTO "cake" ("id", "name") VALUES (1, 'Apple Pie') ON CONFLICT DO NOTHING"#,
);
}
//failed to run
#[test]
fn insert_do_nothing_mysql() {
assert_eq!(
Insert::<cake::ActiveModel>::new()
.add(cake::Model {
id: 1,
name: "Apple Pie".to_owned(),
})
.on_conflict(OnConflict::new()
.do_nothing()
.to_owned()
)
.build(DbBackend::Mysql)
.to_string(),
r#"INSERT IGNORE INTO "cake" ("id", "name") VALUES (1, 'Apple Pie')"#,
);
}
// currently ok
#[test]
fn insert_do_nothing() {
assert_eq!(
Insert::<cake::ActiveModel>::new()
.add(cake::Model {
id: 1,
name: "Apple Pie".to_owned(),
})
.on_conflict(OnConflict::new()
.do_nothing()
.to_owned()
)
.build(DbBackend::Sqlite)
.to_string(),
r#"INSERT IGNORE INTO "cake" ("id", "name") VALUES (1, 'Apple Pie')"#,
);
}
}

View File

@ -285,6 +285,33 @@ where
}
}
impl<M, N> IntoMockRow for (M, Option<N>)
where
M: ModelTrait,
N: ModelTrait,
{
fn into_mock_row(self) -> MockRow {
let mut mapped_join = BTreeMap::new();
for column in <<M as ModelTrait>::Entity as EntityTrait>::Column::iter() {
mapped_join.insert(
format!("{}{}", SelectA.as_str(), column.as_str()),
self.0.get(column),
);
}
if let Some(b_entity) = self.1 {
for column in <<N as ModelTrait>::Entity as EntityTrait>::Column::iter() {
mapped_join.insert(
format!("{}{}", SelectB.as_str(), column.as_str()),
b_entity.get(column),
);
}
}
mapped_join.into_mock_row()
}
}
impl<T> IntoMockRow for BTreeMap<T, Value>
where
T: Into<String>,

View File

@ -960,7 +960,6 @@ mod tests {
);
}
delete_by_id("UUID".to_string());
delete_by_id("UUID".to_string());
delete_by_id("UUID");
delete_by_id(Cow::from("UUID"));

View File

@ -595,7 +595,7 @@ where
///
/// > `SelectTwoMany::one()` method has been dropped (#486)
/// >
/// > You can get `(Entity, Vec<RelatedEntity>)` by first querying a single model from Entity,
/// > You can get `(Entity, Vec<relatedEntity>)` by first querying a single model from Entity,
/// > then use [`ModelTrait::find_related`] on the model.
/// >
/// > See https://www.sea-ql.org/SeaORM/docs/basic-crud/select#lazy-loading for details.
@ -1107,3 +1107,565 @@ where
}
acc
}
#[cfg(test)]
mod tests {
use pretty_assertions::assert_eq;
fn cake_fruit_model(
cake_id: i32,
fruit_id: i32,
) -> (
sea_orm::tests_cfg::cake::Model,
sea_orm::tests_cfg::fruit::Model,
) {
(cake_model(cake_id), fruit_model(fruit_id, Some(cake_id)))
}
fn cake_model(id: i32) -> sea_orm::tests_cfg::cake::Model {
let name = match id {
1 => "apple cake",
2 => "orange cake",
3 => "fruit cake",
4 => "chocolate cake",
_ => "",
}
.to_string();
sea_orm::tests_cfg::cake::Model { id, name }
}
fn fruit_model(id: i32, cake_id: Option<i32>) -> sea_orm::tests_cfg::fruit::Model {
let name = match id {
1 => "apple",
2 => "orange",
3 => "grape",
4 => "strawberry",
_ => "",
}
.to_string();
sea_orm::tests_cfg::fruit::Model { id, name, cake_id }
}
fn cake_vendor_link(
cake_id: i32,
vendor_id: i32,
) -> (
sea_orm::tests_cfg::cake::Model,
sea_orm::tests_cfg::vendor::Model,
) {
(cake_model(cake_id), vendor_model(vendor_id))
}
fn vendor_model(id: i32) -> sea_orm::tests_cfg::vendor::Model {
let name = match id {
1 => "Apollo",
2 => "Benny",
3 => "Christine",
4 => "David",
_ => "",
}
.to_string();
sea_orm::tests_cfg::vendor::Model { id, name }
}
#[smol_potat::test]
pub async fn also_related() -> Result<(), sea_orm::DbErr> {
use sea_orm::tests_cfg::*;
use sea_orm::{DbBackend, EntityTrait, MockDatabase, Statement, Transaction};
let db = MockDatabase::new(DbBackend::Postgres)
.append_query_results([[cake_fruit_model(1, 1)]])
.into_connection();
assert_eq!(
Cake::find().find_also_related(Fruit).all(&db).await?,
[(cake_model(1), Some(fruit_model(1, Some(1))))]
);
assert_eq!(
db.into_transaction_log(),
[Transaction::many([Statement::from_sql_and_values(
DbBackend::Postgres,
[
r#"SELECT "cake"."id" AS "A_id", "cake"."name" AS "A_name","#,
r#""fruit"."id" AS "B_id", "fruit"."name" AS "B_name", "fruit"."cake_id" AS "B_cake_id""#,
r#"FROM "cake""#,
r#"LEFT JOIN "fruit" ON "cake"."id" = "fruit"."cake_id""#,
]
.join(" ")
.as_str(),
[]
),])]
);
Ok(())
}
#[smol_potat::test]
pub async fn also_related_2() -> Result<(), sea_orm::DbErr> {
use sea_orm::tests_cfg::*;
use sea_orm::{DbBackend, EntityTrait, MockDatabase};
let db = MockDatabase::new(DbBackend::Postgres)
.append_query_results([[cake_fruit_model(1, 1), cake_fruit_model(1, 2)]])
.into_connection();
assert_eq!(
Cake::find().find_also_related(Fruit).all(&db).await?,
[
(cake_model(1), Some(fruit_model(1, Some(1)))),
(cake_model(1), Some(fruit_model(2, Some(1))))
]
);
Ok(())
}
#[smol_potat::test]
pub async fn also_related_3() -> Result<(), sea_orm::DbErr> {
use sea_orm::tests_cfg::*;
use sea_orm::{DbBackend, EntityTrait, MockDatabase};
let db = MockDatabase::new(DbBackend::Postgres)
.append_query_results([[
cake_fruit_model(1, 1),
cake_fruit_model(1, 2),
cake_fruit_model(2, 2),
]])
.into_connection();
assert_eq!(
Cake::find().find_also_related(Fruit).all(&db).await?,
[
(cake_model(1), Some(fruit_model(1, Some(1)))),
(cake_model(1), Some(fruit_model(2, Some(1)))),
(cake_model(2), Some(fruit_model(2, Some(2))))
]
);
Ok(())
}
#[smol_potat::test]
pub async fn also_related_4() -> Result<(), sea_orm::DbErr> {
use sea_orm::tests_cfg::*;
use sea_orm::{DbBackend, EntityTrait, IntoMockRow, MockDatabase};
let db = MockDatabase::new(DbBackend::Postgres)
.append_query_results([[
cake_fruit_model(1, 1).into_mock_row(),
cake_fruit_model(1, 2).into_mock_row(),
cake_fruit_model(2, 2).into_mock_row(),
(cake_model(3), None::<fruit::Model>).into_mock_row(),
]])
.into_connection();
assert_eq!(
Cake::find().find_also_related(Fruit).all(&db).await?,
[
(cake_model(1), Some(fruit_model(1, Some(1)))),
(cake_model(1), Some(fruit_model(2, Some(1)))),
(cake_model(2), Some(fruit_model(2, Some(2)))),
(cake_model(3), None)
]
);
Ok(())
}
#[smol_potat::test]
pub async fn with_related() -> Result<(), sea_orm::DbErr> {
use sea_orm::tests_cfg::*;
use sea_orm::{DbBackend, EntityTrait, MockDatabase, Statement, Transaction};
let db = MockDatabase::new(DbBackend::Postgres)
.append_query_results([[
cake_fruit_model(1, 1),
cake_fruit_model(2, 2),
cake_fruit_model(2, 3),
]])
.into_connection();
assert_eq!(
Cake::find().find_with_related(Fruit).all(&db).await?,
[
(cake_model(1), vec![fruit_model(1, Some(1))]),
(
cake_model(2),
vec![fruit_model(2, Some(2)), fruit_model(3, Some(2))]
)
]
);
assert_eq!(
db.into_transaction_log(),
[Transaction::many([Statement::from_sql_and_values(
DbBackend::Postgres,
[
r#"SELECT "cake"."id" AS "A_id", "cake"."name" AS "A_name","#,
r#""fruit"."id" AS "B_id", "fruit"."name" AS "B_name", "fruit"."cake_id" AS "B_cake_id""#,
r#"FROM "cake""#,
r#"LEFT JOIN "fruit" ON "cake"."id" = "fruit"."cake_id""#,
r#"ORDER BY "cake"."id" ASC"#
]
.join(" ")
.as_str(),
[]
),])]
);
Ok(())
}
#[smol_potat::test]
pub async fn with_related_2() -> Result<(), sea_orm::DbErr> {
use sea_orm::tests_cfg::*;
use sea_orm::{DbBackend, EntityTrait, IntoMockRow, MockDatabase};
let db = MockDatabase::new(DbBackend::Postgres)
.append_query_results([[
cake_fruit_model(1, 1).into_mock_row(),
cake_fruit_model(2, 1).into_mock_row(),
cake_fruit_model(2, 2).into_mock_row(),
cake_fruit_model(2, 3).into_mock_row(),
]])
.into_connection();
assert_eq!(
Cake::find().find_with_related(Fruit).all(&db).await?,
[
(cake_model(1), vec![fruit_model(1, Some(1)),]),
(
cake_model(2),
vec![
fruit_model(1, Some(2)),
fruit_model(2, Some(2)),
fruit_model(3, Some(2)),
]
),
]
);
Ok(())
}
#[smol_potat::test]
pub async fn with_related_empty() -> Result<(), sea_orm::DbErr> {
use sea_orm::tests_cfg::*;
use sea_orm::{DbBackend, EntityTrait, IntoMockRow, MockDatabase};
let db = MockDatabase::new(DbBackend::Postgres)
.append_query_results([[
cake_fruit_model(1, 1).into_mock_row(),
cake_fruit_model(2, 1).into_mock_row(),
cake_fruit_model(2, 2).into_mock_row(),
cake_fruit_model(2, 3).into_mock_row(),
(cake_model(3), None::<fruit::Model>).into_mock_row(),
]])
.into_connection();
assert_eq!(
Cake::find().find_with_related(Fruit).all(&db).await?,
[
(cake_model(1), vec![fruit_model(1, Some(1)),]),
(
cake_model(2),
vec![
fruit_model(1, Some(2)),
fruit_model(2, Some(2)),
fruit_model(3, Some(2)),
]
),
(cake_model(3), vec![])
]
);
Ok(())
}
#[smol_potat::test]
pub async fn also_linked_base() -> Result<(), sea_orm::DbErr> {
use sea_orm::tests_cfg::*;
use sea_orm::{DbBackend, EntityTrait, MockDatabase, Statement, Transaction};
let db = MockDatabase::new(DbBackend::Postgres)
.append_query_results([[cake_vendor_link(1, 1)]])
.into_connection();
assert_eq!(
Cake::find()
.find_also_linked(entity_linked::CakeToFillingVendor)
.all(&db)
.await?,
[(cake_model(1), Some(vendor_model(1)))]
);
assert_eq!(
db.into_transaction_log(),
[Transaction::many([Statement::from_sql_and_values(
DbBackend::Postgres,
[
r#"SELECT "cake"."id" AS "A_id", "cake"."name" AS "A_name","#,
r#""r2"."id" AS "B_id", "r2"."name" AS "B_name""#,
r#"FROM "cake""#,
r#"LEFT JOIN "cake_filling" AS "r0" ON "cake"."id" = "r0"."cake_id""#,
r#"LEFT JOIN "filling" AS "r1" ON "r0"."filling_id" = "r1"."id""#,
r#"LEFT JOIN "vendor" AS "r2" ON "r1"."vendor_id" = "r2"."id""#,
]
.join(" ")
.as_str(),
[]
),])]
);
Ok(())
}
#[smol_potat::test]
pub async fn also_linked_same_cake() -> Result<(), sea_orm::DbErr> {
use sea_orm::tests_cfg::*;
use sea_orm::{DbBackend, EntityTrait, MockDatabase};
let db = MockDatabase::new(DbBackend::Postgres)
.append_query_results([[
cake_vendor_link(1, 1),
cake_vendor_link(1, 2),
cake_vendor_link(2, 3),
]])
.into_connection();
assert_eq!(
Cake::find()
.find_also_linked(entity_linked::CakeToFillingVendor)
.all(&db)
.await?,
[
(cake_model(1), Some(vendor_model(1))),
(cake_model(1), Some(vendor_model(2))),
(cake_model(2), Some(vendor_model(3)))
]
);
Ok(())
}
#[smol_potat::test]
pub async fn also_linked_same_vendor() -> Result<(), sea_orm::DbErr> {
use sea_orm::tests_cfg::*;
use sea_orm::{DbBackend, EntityTrait, IntoMockRow, MockDatabase};
let db = MockDatabase::new(DbBackend::Postgres)
.append_query_results([[
cake_vendor_link(1, 1).into_mock_row(),
cake_vendor_link(2, 1).into_mock_row(),
cake_vendor_link(3, 2).into_mock_row(),
]])
.into_connection();
assert_eq!(
Cake::find()
.find_also_linked(entity_linked::CakeToFillingVendor)
.all(&db)
.await?,
[
(cake_model(1), Some(vendor_model(1))),
(cake_model(2), Some(vendor_model(1))),
(cake_model(3), Some(vendor_model(2))),
]
);
Ok(())
}
#[smol_potat::test]
pub async fn also_linked_many_to_many() -> Result<(), sea_orm::DbErr> {
use sea_orm::tests_cfg::*;
use sea_orm::{DbBackend, EntityTrait, IntoMockRow, MockDatabase};
let db = MockDatabase::new(DbBackend::Postgres)
.append_query_results([[
cake_vendor_link(1, 1).into_mock_row(),
cake_vendor_link(1, 2).into_mock_row(),
cake_vendor_link(1, 3).into_mock_row(),
cake_vendor_link(2, 1).into_mock_row(),
cake_vendor_link(2, 2).into_mock_row(),
]])
.into_connection();
assert_eq!(
Cake::find()
.find_also_linked(entity_linked::CakeToFillingVendor)
.all(&db)
.await?,
[
(cake_model(1), Some(vendor_model(1))),
(cake_model(1), Some(vendor_model(2))),
(cake_model(1), Some(vendor_model(3))),
(cake_model(2), Some(vendor_model(1))),
(cake_model(2), Some(vendor_model(2))),
]
);
Ok(())
}
#[smol_potat::test]
pub async fn also_linked_empty() -> Result<(), sea_orm::DbErr> {
use sea_orm::tests_cfg::*;
use sea_orm::{DbBackend, EntityTrait, IntoMockRow, MockDatabase};
let db = MockDatabase::new(DbBackend::Postgres)
.append_query_results([[
cake_vendor_link(1, 1).into_mock_row(),
cake_vendor_link(2, 2).into_mock_row(),
cake_vendor_link(3, 3).into_mock_row(),
(cake_model(4), None::<vendor::Model>).into_mock_row(),
]])
.into_connection();
assert_eq!(
Cake::find()
.find_also_linked(entity_linked::CakeToFillingVendor)
.all(&db)
.await?,
[
(cake_model(1), Some(vendor_model(1))),
(cake_model(2), Some(vendor_model(2))),
(cake_model(3), Some(vendor_model(3))),
(cake_model(4), None)
]
);
Ok(())
}
#[smol_potat::test]
pub async fn with_linked_base() -> Result<(), sea_orm::DbErr> {
use sea_orm::tests_cfg::*;
use sea_orm::{DbBackend, EntityTrait, MockDatabase, Statement, Transaction};
let db = MockDatabase::new(DbBackend::Postgres)
.append_query_results([[
cake_vendor_link(1, 1),
cake_vendor_link(2, 2),
cake_vendor_link(2, 3),
]])
.into_connection();
assert_eq!(
Cake::find()
.find_with_linked(entity_linked::CakeToFillingVendor)
.all(&db)
.await?,
[
(cake_model(1), vec![vendor_model(1)]),
(cake_model(2), vec![vendor_model(2), vendor_model(3)])
]
);
assert_eq!(
db.into_transaction_log(),
[Transaction::many([Statement::from_sql_and_values(
DbBackend::Postgres,
[
r#"SELECT "cake"."id" AS "A_id", "cake"."name" AS "A_name","#,
r#""r2"."id" AS "B_id", "r2"."name" AS "B_name" FROM "cake""#,
r#"LEFT JOIN "cake_filling" AS "r0" ON "cake"."id" = "r0"."cake_id""#,
r#"LEFT JOIN "filling" AS "r1" ON "r0"."filling_id" = "r1"."id""#,
r#"LEFT JOIN "vendor" AS "r2" ON "r1"."vendor_id" = "r2"."id""#,
]
.join(" ")
.as_str(),
[]
),])]
);
Ok(())
}
#[smol_potat::test]
pub async fn with_linked_same_vendor() -> Result<(), sea_orm::DbErr> {
use sea_orm::tests_cfg::*;
use sea_orm::{DbBackend, EntityTrait, IntoMockRow, MockDatabase};
let db = MockDatabase::new(DbBackend::Postgres)
.append_query_results([[
cake_vendor_link(1, 1).into_mock_row(),
cake_vendor_link(2, 2).into_mock_row(),
cake_vendor_link(3, 2).into_mock_row(),
]])
.into_connection();
assert_eq!(
Cake::find()
.find_with_linked(entity_linked::CakeToFillingVendor)
.all(&db)
.await?,
[
(cake_model(1), vec![vendor_model(1)]),
(cake_model(2), vec![vendor_model(2)]),
(cake_model(3), vec![vendor_model(2)])
]
);
Ok(())
}
#[smol_potat::test]
pub async fn with_linked_empty() -> Result<(), sea_orm::DbErr> {
use sea_orm::tests_cfg::*;
use sea_orm::{DbBackend, EntityTrait, IntoMockRow, MockDatabase};
let db = MockDatabase::new(DbBackend::Postgres)
.append_query_results([[
cake_vendor_link(1, 1).into_mock_row(),
cake_vendor_link(2, 1).into_mock_row(),
cake_vendor_link(2, 2).into_mock_row(),
(cake_model(3), None::<vendor::Model>).into_mock_row(),
]])
.into_connection();
assert_eq!(
Cake::find()
.find_with_linked(entity_linked::CakeToFillingVendor)
.all(&db)
.await?,
[
(cake_model(1), vec![vendor_model(1)]),
(cake_model(2), vec![vendor_model(1), vendor_model(2)]),
(cake_model(3), vec![])
]
);
Ok(())
}
// normally would not happen
#[smol_potat::test]
pub async fn with_linked_repeated() -> Result<(), sea_orm::DbErr> {
use sea_orm::tests_cfg::*;
use sea_orm::{DbBackend, EntityTrait, IntoMockRow, MockDatabase};
let db = MockDatabase::new(DbBackend::Postgres)
.append_query_results([[
cake_vendor_link(1, 1).into_mock_row(),
cake_vendor_link(1, 1).into_mock_row(),
cake_vendor_link(2, 1).into_mock_row(),
cake_vendor_link(2, 2).into_mock_row(),
]])
.into_connection();
assert_eq!(
Cake::find()
.find_with_linked(entity_linked::CakeToFillingVendor)
.all(&db)
.await?,
[
(cake_model(1), vec![vendor_model(1), vendor_model(1)]),
(cake_model(2), vec![vendor_model(1), vendor_model(2)]),
]
);
Ok(())
}
}

View File

@ -440,72 +440,138 @@ fn table_column(tbl: &TableRef, col: &DynIden) -> ColumnRef {
#[cfg(test)]
mod tests {
fn cake_model(id: i32) -> sea_orm::tests_cfg::cake::Model {
let name = match id {
1 => "apple cake",
2 => "orange cake",
3 => "fruit cake",
4 => "chocolate cake",
_ => "",
}
.to_string();
sea_orm::tests_cfg::cake::Model { id, name }
}
fn fruit_model(id: i32, cake_id: Option<i32>) -> sea_orm::tests_cfg::fruit::Model {
let name = match id {
1 => "apple",
2 => "orange",
3 => "grape",
4 => "strawberry",
_ => "",
}
.to_string();
sea_orm::tests_cfg::fruit::Model { id, name, cake_id }
}
fn filling_model(id: i32) -> sea_orm::tests_cfg::filling::Model {
let name = match id {
1 => "apple juice",
2 => "orange jam",
3 => "chocolate crust",
4 => "strawberry jam",
_ => "",
}
.to_string();
sea_orm::tests_cfg::filling::Model {
id,
name,
vendor_id: Some(1),
ignored_attr: 0,
}
}
fn cake_filling_model(
cake_id: i32,
filling_id: i32,
) -> sea_orm::tests_cfg::cake_filling::Model {
sea_orm::tests_cfg::cake_filling::Model {
cake_id,
filling_id,
}
}
#[tokio::test]
async fn test_load_one() {
use crate::{
entity::prelude::*, tests_cfg::*, DbBackend, IntoMockRow, LoaderTrait, MockDatabase,
};
use sea_orm::{entity::prelude::*, tests_cfg::*, DbBackend, LoaderTrait, MockDatabase};
let db = MockDatabase::new(DbBackend::Postgres)
.append_query_results([[
cake::Model {
id: 1,
name: "New York Cheese".to_owned(),
}
.into_mock_row(),
cake::Model {
id: 2,
name: "London Cheese".to_owned(),
}
.into_mock_row(),
]])
.append_query_results([[cake_model(1), cake_model(2)]])
.into_connection();
let fruits = vec![fruit::Model {
id: 1,
name: "Apple".to_owned(),
cake_id: Some(1),
}];
let fruits = vec![fruit_model(1, Some(1))];
let cakes = fruits
.load_one(cake::Entity::find(), &db)
.await
.expect("Should return something");
assert_eq!(
cakes,
[Some(cake::Model {
id: 1,
name: "New York Cheese".to_owned(),
})]
);
assert_eq!(cakes, [Some(cake_model(1))]);
}
#[tokio::test]
async fn test_load_one_same_cake() {
use sea_orm::{entity::prelude::*, tests_cfg::*, DbBackend, LoaderTrait, MockDatabase};
let db = MockDatabase::new(DbBackend::Postgres)
.append_query_results([[cake_model(1), cake_model(2)]])
.into_connection();
let fruits = vec![fruit_model(1, Some(1)), fruit_model(2, Some(1))];
let cakes = fruits
.load_one(cake::Entity::find(), &db)
.await
.expect("Should return something");
assert_eq!(cakes, [Some(cake_model(1)), Some(cake_model(1))]);
}
#[tokio::test]
async fn test_load_one_empty() {
use sea_orm::{entity::prelude::*, tests_cfg::*, DbBackend, LoaderTrait, MockDatabase};
let db = MockDatabase::new(DbBackend::Postgres)
.append_query_results([[cake_model(1), cake_model(2)]])
.into_connection();
let fruits: Vec<fruit::Model> = vec![];
let cakes = fruits
.load_one(cake::Entity::find(), &db)
.await
.expect("Should return something");
assert_eq!(cakes, []);
}
#[tokio::test]
async fn test_load_many() {
use crate::{
entity::prelude::*, tests_cfg::*, DbBackend, IntoMockRow, LoaderTrait, MockDatabase,
};
use sea_orm::{entity::prelude::*, tests_cfg::*, DbBackend, LoaderTrait, MockDatabase};
let db = MockDatabase::new(DbBackend::Postgres)
.append_query_results([[fruit::Model {
id: 1,
name: "Apple".to_owned(),
cake_id: Some(1),
}
.into_mock_row()]])
.append_query_results([[fruit_model(1, Some(1))]])
.into_connection();
let cakes = vec![
cake::Model {
id: 1,
name: "New York Cheese".to_owned(),
},
cake::Model {
id: 2,
name: "London Cheese".to_owned(),
},
];
let cakes = vec![cake_model(1), cake_model(2)];
let fruits = cakes
.load_many(fruit::Entity::find(), &db)
.await
.expect("Should return something");
assert_eq!(fruits, [vec![fruit_model(1, Some(1))], vec![]]);
}
#[tokio::test]
async fn test_load_many_same_fruit() {
use sea_orm::{entity::prelude::*, tests_cfg::*, DbBackend, LoaderTrait, MockDatabase};
let db = MockDatabase::new(DbBackend::Postgres)
.append_query_results([[fruit_model(1, Some(1)), fruit_model(2, Some(1))]])
.into_connection();
let cakes = vec![cake_model(1), cake_model(2)];
let fruits = cakes
.load_many(fruit::Entity::find(), &db)
@ -515,13 +581,120 @@ mod tests {
assert_eq!(
fruits,
[
vec![fruit::Model {
id: 1,
name: "Apple".to_owned(),
cake_id: Some(1),
}],
vec![fruit_model(1, Some(1)), fruit_model(2, Some(1))],
vec![]
]
);
}
// FIXME: load many with empty vector will panic
// #[tokio::test]
async fn test_load_many_empty() {
use sea_orm::{entity::prelude::*, tests_cfg::*, DbBackend, MockDatabase};
let db = MockDatabase::new(DbBackend::Postgres)
.append_query_results([[fruit_model(1, Some(1)), fruit_model(2, Some(1))]])
.into_connection();
let cakes: Vec<cake::Model> = vec![];
let fruits = cakes
.load_many(fruit::Entity::find(), &db)
.await
.expect("Should return something");
let empty_vec: Vec<Vec<fruit::Model>> = vec![];
assert_eq!(fruits, empty_vec);
}
#[tokio::test]
async fn test_load_many_to_many_base() {
use sea_orm::{
entity::prelude::*, tests_cfg::*, DbBackend, IntoMockRow, LoaderTrait, MockDatabase,
};
let db = MockDatabase::new(DbBackend::Postgres)
.append_query_results([
[cake_filling_model(1, 1).into_mock_row()],
[filling_model(1).into_mock_row()],
])
.into_connection();
let cakes = vec![cake_model(1)];
let fillings = cakes
.load_many_to_many(Filling, CakeFilling, &db)
.await
.expect("Should return something");
assert_eq!(fillings, vec![vec![filling_model(1)]]);
}
#[tokio::test]
async fn test_load_many_to_many_complex() {
use sea_orm::{
entity::prelude::*, tests_cfg::*, DbBackend, IntoMockRow, LoaderTrait, MockDatabase,
};
let db = MockDatabase::new(DbBackend::Postgres)
.append_query_results([
[
cake_filling_model(1, 1).into_mock_row(),
cake_filling_model(1, 2).into_mock_row(),
cake_filling_model(1, 3).into_mock_row(),
cake_filling_model(2, 1).into_mock_row(),
cake_filling_model(2, 2).into_mock_row(),
],
[
filling_model(1).into_mock_row(),
filling_model(2).into_mock_row(),
filling_model(3).into_mock_row(),
filling_model(4).into_mock_row(),
filling_model(5).into_mock_row(),
],
])
.into_connection();
let cakes = vec![cake_model(1), cake_model(2), cake_model(3)];
let fillings = cakes
.load_many_to_many(Filling, CakeFilling, &db)
.await
.expect("Should return something");
assert_eq!(
fillings,
vec![
vec![filling_model(1), filling_model(2), filling_model(3)],
vec![filling_model(1), filling_model(2)],
vec![],
]
);
}
#[tokio::test]
async fn test_load_many_to_many_empty() {
use sea_orm::{
entity::prelude::*, tests_cfg::*, DbBackend, IntoMockRow, LoaderTrait, MockDatabase,
};
let db = MockDatabase::new(DbBackend::Postgres)
.append_query_results([
[cake_filling_model(1, 1).into_mock_row()],
[filling_model(1).into_mock_row()],
])
.into_connection();
let cakes: Vec<cake::Model> = vec![];
let fillings = cakes
.load_many_to_many(Filling, CakeFilling, &db)
.await
.expect("Should return something");
let empty_vec: Vec<Vec<filling::Model>> = vec![];
assert_eq!(fillings, empty_vec);
}
}

View File

@ -371,6 +371,27 @@ pub async fn find_linked_active_enum(db: &DatabaseConnection) -> Result<(), DbEr
})
)]
);
assert_eq!(
ActiveEnum::find()
.find_with_linked(active_enum::ActiveEnumChildLink)
.all(db)
.await?,
[(
active_enum::Model {
id: 2,
category: Some(Category::Small),
color: Some(Color::White),
tea: Some(Tea::BreakfastTea),
},
vec![active_enum_child::Model {
id: 1,
parent_id: 2,
category: Some(Category::Big),
color: Some(Color::Black),
tea: Some(Tea::EverydayTea),
}]
)]
);
assert_eq!(
active_enum_child::Model {
@ -411,6 +432,27 @@ pub async fn find_linked_active_enum(db: &DatabaseConnection) -> Result<(), DbEr
})
)]
);
assert_eq!(
ActiveEnumChild::find()
.find_with_linked(active_enum_child::ActiveEnumLink)
.all(db)
.await?,
[(
active_enum_child::Model {
id: 1,
parent_id: 2,
category: Some(Category::Big),
color: Some(Color::Black),
tea: Some(Tea::EverydayTea),
},
vec![active_enum::Model {
id: 2,
category: Some(Category::Small),
color: Some(Color::White),
tea: Some(Tea::BreakfastTea),
}]
)]
);
Ok(())
}

View File

@ -52,6 +52,7 @@ pub async fn test_update_cake(db: &DbConn) {
let cake_model = cake.unwrap();
assert_eq!(cake_model.name, "Extra chocolate mud cake");
assert_eq!(cake_model.price, dec!(20.00));
assert!(!cake_model.gluten_free);
}
pub async fn test_update_bakery(db: &DbConn) {

View File

@ -9,6 +9,7 @@ pub use sea_orm::{
pub use crud::*;
// use common::bakery_chain::*;
use sea_orm::{DbConn, TryInsertResult};
use sea_query::OnConflict;
#[sea_orm_macros::test]
#[cfg(any(
@ -37,6 +38,12 @@ pub async fn test(db: &DbConn) {
assert!(matches!(res, Ok(TryInsertResult::Inserted(_))));
let double_seaside_bakery = bakery::ActiveModel {
name: Set("SeaSide Bakery".to_owned()),
profit_margin: Set(10.4),
id: Set(1),
};
let empty_insert = Bakery::insert_many(std::iter::empty::<bakery::ActiveModel>())
.on_empty_do_nothing()
.exec(db)

View File

@ -1,7 +1,7 @@
pub mod common;
pub use common::{bakery_chain::*, setup::*, TestContext};
use sea_orm::{entity::*, query::*, DbConn, DbErr};
use sea_orm::{entity::*, query::*, DbConn, DbErr, RuntimeErr};
#[sea_orm_macros::test]
#[cfg(any(
@ -32,6 +32,17 @@ async fn loader_load_one() -> Result<(), DbErr> {
assert_eq!(bakers, [baker_1, baker_2, baker_3]);
assert_eq!(bakeries, [Some(bakery_0.clone()), Some(bakery_0), None]);
// has many find, should use load_many instead
let bakeries = bakery::Entity::find().all(&ctx.db).await?;
let bakers = bakeries.load_one(baker::Entity, &ctx.db).await;
assert_eq!(
bakers,
Err(DbErr::Query(RuntimeErr::Internal(
"Relation is HasMany instead of HasOne".to_string()
)))
);
Ok(())
}
@ -47,6 +58,7 @@ async fn loader_load_many() -> Result<(), DbErr> {
let bakery_1 = insert_bakery(&ctx.db, "SeaSide Bakery").await?;
let bakery_2 = insert_bakery(&ctx.db, "Offshore Bakery").await?;
let bakery_3 = insert_bakery(&ctx.db, "Rocky Bakery").await?;
let baker_1 = insert_baker(&ctx.db, "Baker 1", bakery_1.id).await?;
let baker_2 = insert_baker(&ctx.db, "Baker 2", bakery_1.id).await?;
@ -57,12 +69,16 @@ async fn loader_load_many() -> Result<(), DbErr> {
let bakeries = bakery::Entity::find().all(&ctx.db).await?;
let bakers = bakeries.load_many(baker::Entity, &ctx.db).await?;
assert_eq!(bakeries, [bakery_1.clone(), bakery_2.clone()]);
assert_eq!(
bakeries,
[bakery_1.clone(), bakery_2.clone(), bakery_3.clone()]
);
assert_eq!(
bakers,
[
[baker_1.clone(), baker_2.clone()],
[baker_3.clone(), baker_4.clone()]
vec![baker_1.clone(), baker_2.clone()],
vec![baker_3.clone(), baker_4.clone()],
vec![]
]
);
@ -79,7 +95,8 @@ async fn loader_load_many() -> Result<(), DbErr> {
bakers,
[
vec![baker_1.clone(), baker_2.clone()],
vec![baker_4.clone()]
vec![baker_4.clone()],
vec![]
]
);
@ -150,6 +167,7 @@ async fn loader_load_many_to_many() -> Result<(), DbErr> {
let baker_1 = insert_baker(&ctx.db, "Jane", bakery_1.id).await?;
let baker_2 = insert_baker(&ctx.db, "Peter", bakery_1.id).await?;
let baker_3 = insert_baker(&ctx.db, "Fred", bakery_1.id).await?; // does not make cake
let cake_1 = insert_cake(&ctx.db, "Cheesecake", None).await?;
let cake_2 = insert_cake(&ctx.db, "Coffee", None).await?;
@ -166,12 +184,13 @@ async fn loader_load_many_to_many() -> Result<(), DbErr> {
.load_many_to_many(cake::Entity, cakes_bakers::Entity, &ctx.db)
.await?;
assert_eq!(bakers, [baker_1.clone(), baker_2.clone()]);
assert_eq!(bakers, [baker_1.clone(), baker_2.clone(), baker_3.clone()]);
assert_eq!(
cakes,
[
vec![cake_1.clone(), cake_2.clone()],
vec![cake_2.clone(), cake_3.clone()]
vec![cake_2.clone(), cake_3.clone()],
vec![]
]
);
@ -184,7 +203,7 @@ async fn loader_load_many_to_many() -> Result<(), DbErr> {
&ctx.db,
)
.await?;
assert_eq!(cakes, [vec![cake_1.clone()], vec![cake_3.clone()]]);
assert_eq!(cakes, [vec![cake_1.clone()], vec![cake_3.clone()], vec![]]);
// now, start again from cakes

View File

@ -494,6 +494,255 @@ pub async fn having() {
ctx.delete().await;
}
#[sea_orm_macros::test]
#[cfg(any(
feature = "sqlx-mysql",
feature = "sqlx-sqlite",
feature = "sqlx-postgres"
))]
pub async fn related() -> Result<(), DbErr> {
use sea_orm::{SelectA, SelectB};
let ctx = TestContext::new("test_related").await;
create_tables(&ctx.db).await?;
// SeaSide Bakery
let seaside_bakery = bakery::ActiveModel {
name: Set("SeaSide Bakery".to_owned()),
profit_margin: Set(10.4),
..Default::default()
};
let seaside_bakery_res = Bakery::insert(seaside_bakery).exec(&ctx.db).await?;
// Bob's 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(seaside_bakery_res.last_insert_id)),
..Default::default()
};
let _baker_bob_res = Baker::insert(baker_bob).exec(&ctx.db).await?;
// Bobby's Baker
let baker_bobby = baker::ActiveModel {
name: Set("Baker Bobby".to_owned()),
contact_details: Set(serde_json::json!({
"mobile": "+85212345678",
})),
bakery_id: Set(Some(seaside_bakery_res.last_insert_id)),
..Default::default()
};
let _baker_bobby_res = Baker::insert(baker_bobby).exec(&ctx.db).await?;
// Terres Bakery
let terres_bakery = bakery::ActiveModel {
name: Set("Terres Bakery".to_owned()),
profit_margin: Set(13.5),
..Default::default()
};
let terres_bakery_res = Bakery::insert(terres_bakery).exec(&ctx.db).await?;
// Ada's Baker
let baker_ada = baker::ActiveModel {
name: Set("Baker Ada".to_owned()),
contact_details: Set(serde_json::json!({
"mobile": "+61424000000",
"home": "0395555555",
"address": "12 Test St, Testville, Vic, Australia"
})),
bakery_id: Set(Some(terres_bakery_res.last_insert_id)),
..Default::default()
};
let _baker_ada_res = Baker::insert(baker_ada).exec(&ctx.db).await?;
// Stone Bakery, with no baker
let stone_bakery = bakery::ActiveModel {
name: Set("Stone Bakery".to_owned()),
profit_margin: Set(13.5),
..Default::default()
};
let _stone_bakery_res = Bakery::insert(stone_bakery).exec(&ctx.db).await?;
#[derive(Debug, FromQueryResult, PartialEq)]
struct BakerLite {
name: String,
}
#[derive(Debug, FromQueryResult, PartialEq)]
struct BakeryLite {
name: String,
}
// get all bakery and baker's name and put them into tuples
let bakers_in_bakery: Vec<(BakeryLite, Option<BakerLite>)> = Bakery::find()
.find_also_related(Baker)
.select_only()
.column_as(bakery::Column::Name, (SelectA, bakery::Column::Name))
.column_as(baker::Column::Name, (SelectB, baker::Column::Name))
.order_by_asc(bakery::Column::Id)
.order_by_asc(baker::Column::Id)
.into_model()
.all(&ctx.db)
.await?;
assert_eq!(
bakers_in_bakery,
[
(
BakeryLite {
name: "SeaSide Bakery".to_owned(),
},
Some(BakerLite {
name: "Baker Bob".to_owned(),
})
),
(
BakeryLite {
name: "SeaSide Bakery".to_owned(),
},
Some(BakerLite {
name: "Baker Bobby".to_owned(),
})
),
(
BakeryLite {
name: "Terres Bakery".to_owned(),
},
Some(BakerLite {
name: "Baker Ada".to_owned(),
})
),
(
BakeryLite {
name: "Stone Bakery".to_owned(),
},
None,
),
]
);
let seaside_bakery = Bakery::find()
.filter(bakery::Column::Id.eq(1))
.one(&ctx.db)
.await?
.unwrap();
let bakers = seaside_bakery.find_related(Baker).all(&ctx.db).await?;
assert_eq!(
bakers,
[
baker::Model {
id: 1,
name: "Baker Bob".to_owned(),
contact_details: serde_json::json!({
"mobile": "+61424000000",
"home": "0395555555",
"address": "12 Test St, Testville, Vic, Australia"
}),
bakery_id: Some(1),
},
baker::Model {
id: 2,
name: "Baker Bobby".to_owned(),
contact_details: serde_json::json!({
"mobile": "+85212345678",
}),
bakery_id: Some(1),
}
]
);
let select_bakery_with_baker = Bakery::find()
.find_with_related(Baker)
.order_by_asc(baker::Column::Id);
assert_eq!(
select_bakery_with_baker
.build(sea_orm::DatabaseBackend::MySql)
.to_string(),
[
"SELECT `bakery`.`id` AS `A_id`,",
"`bakery`.`name` AS `A_name`,",
"`bakery`.`profit_margin` AS `A_profit_margin`,",
"`baker`.`id` AS `B_id`,",
"`baker`.`name` AS `B_name`,",
"`baker`.`contact_details` AS `B_contact_details`,",
"`baker`.`bakery_id` AS `B_bakery_id`",
"FROM `bakery`",
"LEFT JOIN `baker` ON `bakery`.`id` = `baker`.`bakery_id`",
"ORDER BY `bakery`.`id` ASC, `baker`.`id` ASC"
]
.join(" ")
);
assert_eq!(
select_bakery_with_baker.all(&ctx.db).await?,
[
(
bakery::Model {
id: 1,
name: "SeaSide Bakery".to_owned(),
profit_margin: 10.4,
},
vec![
baker::Model {
id: 1,
name: "Baker Bob".to_owned(),
contact_details: serde_json::json!({
"mobile": "+61424000000",
"home": "0395555555",
"address": "12 Test St, Testville, Vic, Australia"
}),
bakery_id: Some(seaside_bakery_res.last_insert_id),
},
baker::Model {
id: 2,
name: "Baker Bobby".to_owned(),
contact_details: serde_json::json!({
"mobile": "+85212345678",
}),
bakery_id: Some(seaside_bakery_res.last_insert_id),
}
]
),
(
bakery::Model {
id: 2,
name: "Terres Bakery".to_owned(),
profit_margin: 13.5,
},
vec![baker::Model {
id: 3,
name: "Baker Ada".to_owned(),
contact_details: serde_json::json!({
"mobile": "+61424000000",
"home": "0395555555",
"address": "12 Test St, Testville, Vic, Australia"
}),
bakery_id: Some(terres_bakery_res.last_insert_id),
}]
),
(
bakery::Model {
id: 3,
name: "Stone Bakery".to_owned(),
profit_margin: 13.5,
},
vec![]
),
]
);
ctx.delete().await;
Ok(())
}
#[sea_orm_macros::test]
#[cfg(any(
feature = "sqlx-mysql",
@ -586,6 +835,17 @@ pub async fn linked() -> Result<(), DbErr> {
.exec(&ctx.db)
.await?;
// Freerider's Baker, no cake baked
let baker_freerider = baker::ActiveModel {
name: Set("Freerider".to_owned()),
contact_details: Set(serde_json::json!({
"mobile": "+85298765432",
})),
bakery_id: Set(Some(seaside_bakery_res.last_insert_id)),
..Default::default()
};
let _baker_freerider_res = Baker::insert(baker_freerider).exec(&ctx.db).await?;
// Kate's Customer, Order & Line Item
let customer_kate = customer::ActiveModel {
name: Set("Kate".to_owned()),
@ -680,6 +940,7 @@ pub async fn linked() -> Result<(), DbErr> {
name: String,
}
// filtered find
let baked_for_customers: Vec<(BakerLite, Option<CustomerLite>)> = Baker::find()
.find_also_linked(baker::BakedForCustomer)
.select_only()
@ -725,9 +986,16 @@ pub async fn linked() -> Result<(), DbErr> {
name: "Kara".to_owned(),
})
),
(
BakerLite {
name: "Freerider".to_owned(),
},
None,
),
]
);
// try to use find_linked instead
let baker_bob = Baker::find()
.filter(baker::Column::Id.eq(1))
.one(&ctx.db)
@ -748,6 +1016,7 @@ pub async fn linked() -> Result<(), DbErr> {
}]
);
// find full model using with_linked
let select_baker_with_customer = Baker::find()
.find_with_linked(baker::BakedForCustomer)
.order_by_asc(baker::Column::Id)
@ -823,6 +1092,17 @@ pub async fn linked() -> Result<(), DbErr> {
},
]
),
(
baker::Model {
id: 3,
name: "Freerider".into(),
contact_details: serde_json::json!({
"mobile": "+85298765432",
}),
bakery_id: Some(1),
},
vec![]
),
]
);

View File

@ -64,4 +64,7 @@ pub async fn test_error(db: &DatabaseConnection) {
fk_error.sql_err(),
Some(SqlErr::ForeignKeyConstraintViolation(_))
));
let invalid_error = DbErr::Custom("random error".to_string());
assert_eq!(invalid_error.sql_err(), None)
}

View File

@ -71,6 +71,7 @@ pub fn type_test() {
assert_eq!(Integer::array_type(), ArrayType::Int);
assert_eq!(Boolbean::column_type(), ColumnType::Boolean);
assert_eq!(Boolbean::array_type(), ArrayType::Bool);
// self implied
assert_eq!(
StringVec::column_type(),