Use MockDatabase in doc test

This commit is contained in:
Chris Tsang 2021-06-27 15:57:22 +08:00
parent a0db19758b
commit 688891f706
6 changed files with 154 additions and 159 deletions

View File

@ -1,4 +1,4 @@
use crate::{ExecErr, ExecResult, QueryErr, QueryResult, Statement}; use crate::{ExecErr, ExecResult, QueryErr, QueryResult, Statement, Transaction};
use sea_query::{ use sea_query::{
MysqlQueryBuilder, PostgresQueryBuilder, QueryStatementBuilder, SqliteQueryBuilder, MysqlQueryBuilder, PostgresQueryBuilder, QueryStatementBuilder, SqliteQueryBuilder,
}; };
@ -118,6 +118,11 @@ impl DatabaseConnection {
pub fn as_mock_connection(&self) -> Option<bool> { pub fn as_mock_connection(&self) -> Option<bool> {
None None
} }
pub fn into_transaction_log(self) -> Vec<Transaction> {
let mut mocker = self.as_mock_connection().get_mocker_mutex().lock().unwrap();
mocker.drain_transaction_log()
}
} }
impl QueryBuilderBackend { impl QueryBuilderBackend {

View File

@ -1,4 +1,5 @@
use crate::Statement; use crate::Statement;
use sea_query::{Value, Values};
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct Transaction { pub struct Transaction {
@ -6,6 +7,16 @@ pub struct Transaction {
} }
impl Transaction { impl Transaction {
pub fn from_sql_and_values<I>(sql: &str, values: I) -> Self
where
I: IntoIterator<Item = Value>,
{
Self::one(Statement {
sql: sql.to_owned(),
values: Some(Values(values.into_iter().collect())),
})
}
/// Create a Transaction with one statement /// Create a Transaction with one statement
pub fn one(stmt: Statement) -> Self { pub fn one(stmt: Statement) -> Self {
Self { stmts: vec![stmt] } Self { stmts: vec![stmt] }

View File

@ -42,17 +42,6 @@ impl Default for ActiveValueState {
} }
} }
pub trait OneOrManyActiveModel<A>
where
A: ActiveModelTrait,
{
fn is_one() -> bool;
fn get_one(self) -> A;
fn is_many() -> bool;
fn get_many(self) -> Vec<A>;
}
#[doc(hidden)] #[doc(hidden)]
pub fn unchanged_active_value_not_intended_for_public_use<V>(value: V) -> ActiveValue<V> pub fn unchanged_active_value_not_intended_for_public_use<V>(value: V) -> ActiveValue<V>
where where
@ -197,44 +186,6 @@ where
} }
} }
impl<A> OneOrManyActiveModel<A> for A
where
A: ActiveModelTrait,
{
fn is_one() -> bool {
true
}
fn get_one(self) -> A {
self
}
fn is_many() -> bool {
false
}
fn get_many(self) -> Vec<A> {
panic!("not many")
}
}
impl<A> OneOrManyActiveModel<A> for Vec<A>
where
A: ActiveModelTrait,
{
fn is_one() -> bool {
false
}
fn get_one(self) -> A {
panic!("not one")
}
fn is_many() -> bool {
true
}
fn get_many(self) -> Vec<A> {
self
}
}
/// Insert the model if primary key is unset, update otherwise. /// Insert the model if primary key is unset, update otherwise.
/// Only works if the entity has auto increment primary key. /// Only works if the entity has auto increment primary key.
pub async fn save_active_model<A, E>(mut am: A, db: &DatabaseConnection) -> Result<A, ExecErr> pub async fn save_active_model<A, E>(mut am: A, db: &DatabaseConnection) -> Result<A, ExecErr>

View File

@ -1,7 +1,7 @@
use crate::{ use crate::{
ActiveModelTrait, ColumnTrait, Delete, DeleteOne, FromQueryResult, Insert, ModelTrait, ActiveModelTrait, ColumnTrait, Delete, DeleteOne, FromQueryResult, Insert, ModelTrait,
OneOrManyActiveModel, PrimaryKeyToColumn, PrimaryKeyTrait, QueryFilter, Related, PrimaryKeyToColumn, PrimaryKeyTrait, QueryFilter, Related, RelationBuilder, RelationTrait,
RelationBuilder, RelationTrait, RelationType, Select, Update, UpdateOne, RelationType, Select, Update, UpdateMany, UpdateOne,
}; };
use sea_query::{Iden, IntoValueTuple}; use sea_query::{Iden, IntoValueTuple};
use std::fmt::Debug; use std::fmt::Debug;
@ -50,14 +50,27 @@ pub trait EntityTrait: EntityName {
} }
/// ``` /// ```
/// use sea_orm::{entity::*, query::*, tests_cfg::cake, sea_query::PostgresQueryBuilder}; /// # #[cfg(feature = "mock")]
/// # use sea_orm::{MockDatabase, Transaction};
/// # let db = MockDatabase::new().into_connection();
/// #
/// use sea_orm::{entity::*, query::*, tests_cfg::cake};
///
/// # async_std::task::block_on(async {
/// cake::Entity::find().one(&db).await;
/// cake::Entity::find().all(&db).await;
/// # });
/// ///
/// assert_eq!( /// assert_eq!(
/// cake::Entity::find() /// db.into_transaction_log(),
/// .build(PostgresQueryBuilder) /// vec![
/// .to_string(), /// Transaction::from_sql_and_values(
/// r#"SELECT "cake"."id", "cake"."name" FROM "cake""# /// r#"SELECT "cake"."id", "cake"."name" FROM "cake" LIMIT $1"#, vec![1u64.into()]
/// ); /// ),
/// Transaction::from_sql_and_values(
/// r#"SELECT "cake"."id", "cake"."name" FROM "cake""#, vec![]
/// ),
/// ]);
/// ``` /// ```
fn find() -> Select<Self> { fn find() -> Select<Self> {
Select::new() Select::new()
@ -65,28 +78,42 @@ pub trait EntityTrait: EntityName {
/// Find a model by primary key /// Find a model by primary key
/// ``` /// ```
/// use sea_orm::{entity::*, query::*, tests_cfg::cake, sea_query::PostgresQueryBuilder}; /// # #[cfg(feature = "mock")]
/// # use sea_orm::{MockDatabase, Transaction};
/// # let db = MockDatabase::new().into_connection();
/// #
/// use sea_orm::{entity::*, query::*, tests_cfg::cake};
///
/// # async_std::task::block_on(async {
/// cake::Entity::find_by_id(11).all(&db).await;
/// # });
/// ///
/// assert_eq!( /// assert_eq!(
/// cake::Entity::find_by_id(11) /// db.into_transaction_log(),
/// .build(PostgresQueryBuilder) /// vec![Transaction::from_sql_and_values(
/// .to_string(), /// r#"SELECT "cake"."id", "cake"."name" FROM "cake" WHERE "cake"."id" = $1"#, vec![11i32.into()]
/// r#"SELECT "cake"."id", "cake"."name" FROM "cake" WHERE "cake"."id" = 11"# /// )]);
/// );
/// ``` /// ```
/// Find by composite key /// Find by composite key
/// ``` /// ```
/// use sea_orm::{entity::*, query::*, tests_cfg::cake_filling, sea_query::PostgresQueryBuilder}; /// # #[cfg(feature = "mock")]
/// # use sea_orm::{MockDatabase, Transaction};
/// # let db = MockDatabase::new().into_connection();
/// #
/// use sea_orm::{entity::*, query::*, tests_cfg::cake_filling};
///
/// # async_std::task::block_on(async {
/// cake_filling::Entity::find_by_id((2, 3)).all(&db).await;
/// # });
/// ///
/// assert_eq!( /// assert_eq!(
/// cake_filling::Entity::find_by_id((2, 3)) /// db.into_transaction_log(),
/// .build(PostgresQueryBuilder) /// vec![Transaction::from_sql_and_values([
/// .to_string(),
/// [
/// r#"SELECT "cake_filling"."cake_id", "cake_filling"."filling_id" FROM "cake_filling""#, /// r#"SELECT "cake_filling"."cake_id", "cake_filling"."filling_id" FROM "cake_filling""#,
/// r#"WHERE "cake_filling"."cake_id" = 2 AND "cake_filling"."filling_id" = 3"#, /// r#"WHERE "cake_filling"."cake_id" = $1 AND "cake_filling"."filling_id" = $2"#,
/// ].join(" ") /// ].join(" ").as_str(),
/// ); /// vec![2i32.into(), 3i32.into()]
/// )]);
/// ``` /// ```
fn find_by_id<V>(values: V) -> Select<Self> fn find_by_id<V>(values: V) -> Select<Self>
where where
@ -108,69 +135,29 @@ pub trait EntityTrait: EntityName {
select select
} }
/// Insert one
/// ``` /// ```
/// use sea_orm::{entity::*, query::*, tests_cfg::cake, sea_query::PostgresQueryBuilder}; /// # #[cfg(feature = "mock")]
/// # use sea_orm::{MockDatabase, Transaction};
/// # let db = MockDatabase::new().into_connection();
/// #
/// use sea_orm::{entity::*, query::*, tests_cfg::cake};
/// ///
/// let apple = cake::ActiveModel { /// let apple = cake::ActiveModel {
/// name: Set("Apple Pie".to_owned()), /// name: Set("Apple Pie".to_owned()),
/// ..Default::default() /// ..Default::default()
/// }; /// };
/// assert_eq!(
/// cake::Entity::insert(apple)
/// .build(PostgresQueryBuilder)
/// .to_string(),
/// r#"INSERT INTO "cake" ("name") VALUES ('Apple Pie')"#,
/// );
/// ```
/// Insert many
/// ```
/// use sea_orm::{entity::*, query::*, tests_cfg::cake, sea_query::PostgresQueryBuilder};
/// ///
/// let apple = cake::ActiveModel { /// # async_std::task::block_on(async {
/// name: Set("Apple Pie".to_owned()), /// cake::Entity::insert(apple).exec(&db).await;
/// ..Default::default() /// # });
/// };
/// let orange = cake::ActiveModel {
/// name: Set("Orange Scone".to_owned()),
/// ..Default::default()
/// };
/// assert_eq!(
/// cake::Entity::insert(vec![apple, orange])
/// .build(PostgresQueryBuilder)
/// .to_string(),
/// r#"INSERT INTO "cake" ("name") VALUES ('Apple Pie'), ('Orange Scone')"#,
/// );
/// ```
fn insert<A, C>(models: C) -> Insert<A>
where
A: ActiveModelTrait<Entity = Self>,
C: OneOrManyActiveModel<A>,
{
if C::is_one() {
Self::insert_one(models.get_one())
} else if C::is_many() {
Self::insert_many(models.get_many())
} else {
unreachable!()
}
}
/// ```
/// use sea_orm::{entity::*, query::*, tests_cfg::cake, sea_query::PostgresQueryBuilder};
/// ///
/// let apple = cake::ActiveModel {
/// name: Set("Apple Pie".to_owned()),
/// ..Default::default()
/// };
/// assert_eq!( /// assert_eq!(
/// cake::Entity::insert_one(apple) /// db.into_transaction_log(),
/// .build(PostgresQueryBuilder) /// vec![Transaction::from_sql_and_values(
/// .to_string(), /// r#"INSERT INTO "cake" ("name") VALUES ($1)"#, vec!["Apple Pie".into()]
/// r#"INSERT INTO "cake" ("name") VALUES ('Apple Pie')"#, /// )]);
/// );
/// ``` /// ```
fn insert_one<A>(model: A) -> Insert<A> fn insert<A>(model: A) -> Insert<A>
where where
A: ActiveModelTrait<Entity = Self>, A: ActiveModelTrait<Entity = Self>,
{ {
@ -178,7 +165,11 @@ pub trait EntityTrait: EntityName {
} }
/// ``` /// ```
/// use sea_orm::{entity::*, query::*, tests_cfg::cake, sea_query::PostgresQueryBuilder}; /// # #[cfg(feature = "mock")]
/// # use sea_orm::{MockDatabase, Transaction};
/// # let db = MockDatabase::new().into_connection();
/// #
/// use sea_orm::{entity::*, query::*, tests_cfg::cake};
/// ///
/// let apple = cake::ActiveModel { /// let apple = cake::ActiveModel {
/// name: Set("Apple Pie".to_owned()), /// name: Set("Apple Pie".to_owned()),
@ -188,12 +179,17 @@ pub trait EntityTrait: EntityName {
/// name: Set("Orange Scone".to_owned()), /// name: Set("Orange Scone".to_owned()),
/// ..Default::default() /// ..Default::default()
/// }; /// };
///
/// # async_std::task::block_on(async {
/// cake::Entity::insert_many(vec![apple, orange]).exec(&db).await;
/// # });
///
/// assert_eq!( /// assert_eq!(
/// cake::Entity::insert_many(vec![apple, orange]) /// db.into_transaction_log(),
/// .build(PostgresQueryBuilder) /// vec![Transaction::from_sql_and_values(
/// .to_string(), /// r#"INSERT INTO "cake" ("name") VALUES ($1), ($2)"#,
/// r#"INSERT INTO "cake" ("name") VALUES ('Apple Pie'), ('Orange Scone')"#, /// vec!["Apple Pie".into(), "Orange Scone".into()]
/// ); /// )]);
/// ``` /// ```
fn insert_many<A, I>(models: I) -> Insert<A> fn insert_many<A, I>(models: I) -> Insert<A>
where where
@ -204,19 +200,27 @@ pub trait EntityTrait: EntityName {
} }
/// ``` /// ```
/// use sea_orm::{entity::*, query::*, tests_cfg::fruit, sea_query::PostgresQueryBuilder}; /// # #[cfg(feature = "mock")]
/// # use sea_orm::{MockDatabase, Transaction};
/// # let db = MockDatabase::new().into_connection();
/// #
/// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
/// ///
/// let orange = fruit::ActiveModel { /// let orange = fruit::ActiveModel {
/// id: Set(1), /// id: Set(1),
/// name: Set("Orange".to_owned()), /// name: Set("Orange".to_owned()),
/// ..Default::default() /// ..Default::default()
/// }; /// };
///
/// # async_std::task::block_on(async {
/// fruit::Entity::update(orange).exec(&db).await;
/// # });
///
/// assert_eq!( /// assert_eq!(
/// fruit::Entity::update(orange) /// db.into_transaction_log(),
/// .build(PostgresQueryBuilder) /// vec![Transaction::from_sql_and_values(
/// .to_string(), /// r#"UPDATE "fruit" SET "name" = $1 WHERE "fruit"."id" = $2"#, vec!["Orange".into(), 1i32.into()]
/// r#"UPDATE "fruit" SET "name" = 'Orange' WHERE "fruit"."id" = 1"#, /// )]);
/// );
/// ``` /// ```
fn update<A>(model: A) -> UpdateOne<A> fn update<A>(model: A) -> UpdateOne<A>
where where
@ -226,18 +230,51 @@ pub trait EntityTrait: EntityName {
} }
/// ``` /// ```
/// use sea_orm::{entity::*, query::*, tests_cfg::fruit, sea_query::PostgresQueryBuilder}; /// # #[cfg(feature = "mock")]
/// # use sea_orm::{MockDatabase, Transaction};
/// # let db = MockDatabase::new().into_connection();
/// #
/// use sea_orm::{entity::*, query::*, tests_cfg::{cake, fruit}, sea_query::{Expr, Value}};
///
/// # async_std::task::block_on(async {
/// fruit::Entity::update_many()
/// .col_expr(fruit::Column::CakeId, Expr::value(Value::Null))
/// .filter(fruit::Column::Name.contains("Apple"))
/// .exec(&db)
/// .await;
/// # });
///
/// assert_eq!(
/// db.into_transaction_log(),
/// vec![Transaction::from_sql_and_values(
/// r#"UPDATE "fruit" SET "cake_id" = $1 WHERE "fruit"."name" LIKE $2"#, vec![Value::Null, "%Apple%".into()]
/// )]);
/// ```
fn update_many() -> UpdateMany<Self> {
Update::many(Self::default())
}
/// ```
/// # #[cfg(feature = "mock")]
/// # use sea_orm::{MockDatabase, Transaction};
/// # let db = MockDatabase::new().into_connection();
/// #
/// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
/// ///
/// let orange = fruit::ActiveModel { /// let orange = fruit::ActiveModel {
/// id: Set(3), /// id: Set(3),
/// ..Default::default() /// ..Default::default()
/// }; /// };
///
/// # async_std::task::block_on(async {
/// fruit::Entity::delete(orange).exec(&db).await;
/// # });
///
/// assert_eq!( /// assert_eq!(
/// fruit::Entity::delete(orange) /// db.into_transaction_log(),
/// .build(PostgresQueryBuilder) /// vec![Transaction::from_sql_and_values(
/// .to_string(), /// r#"DELETE FROM "fruit" WHERE "fruit"."id" = $1"#, vec![3i32.into()]
/// r#"DELETE FROM "fruit" WHERE "fruit"."id" = 3"#, /// )]);
/// );
/// ``` /// ```
fn delete<A>(model: A) -> DeleteOne<A> fn delete<A>(model: A) -> DeleteOne<A>
where where

View File

@ -175,9 +175,7 @@ mod tests {
query_builder.build(select.offset(4).limit(2)), query_builder.build(select.offset(4).limit(2)),
]; ];
let mut mocker = db.as_mock_connection().get_mocker_mutex().lock().unwrap(); assert_eq!(db.into_transaction_log(), Transaction::wrap(stmts));
assert_eq!(mocker.drain_transaction_log(), Transaction::wrap(stmts));
Ok(()) Ok(())
} }
@ -211,9 +209,7 @@ mod tests {
query_builder.build(select.offset(4).limit(2)), query_builder.build(select.offset(4).limit(2)),
]; ];
let mut mocker = db.as_mock_connection().get_mocker_mutex().lock().unwrap(); assert_eq!(db.into_transaction_log(), Transaction::wrap(stmts));
assert_eq!(mocker.drain_transaction_log(), Transaction::wrap(stmts));
Ok(()) Ok(())
} }
@ -244,9 +240,8 @@ mod tests {
let query_builder = db.get_query_builder_backend(); let query_builder = db.get_query_builder_backend();
let stmts = vec![query_builder.build(&select)]; let stmts = vec![query_builder.build(&select)];
let mut mocker = db.as_mock_connection().get_mocker_mutex().lock().unwrap();
assert_eq!(mocker.drain_transaction_log(), Transaction::wrap(stmts)); assert_eq!(db.into_transaction_log(), Transaction::wrap(stmts));
Ok(()) Ok(())
} }
@ -297,9 +292,7 @@ mod tests {
query_builder.build(select.offset(4).limit(2)), query_builder.build(select.offset(4).limit(2)),
]; ];
let mut mocker = db.as_mock_connection().get_mocker_mutex().lock().unwrap(); assert_eq!(db.into_transaction_log(), Transaction::wrap(stmts));
assert_eq!(mocker.drain_transaction_log(), Transaction::wrap(stmts));
Ok(()) Ok(())
} }
@ -331,9 +324,7 @@ mod tests {
query_builder.build(select.offset(4).limit(2)), query_builder.build(select.offset(4).limit(2)),
]; ];
let mut mocker = db.as_mock_connection().get_mocker_mutex().lock().unwrap(); assert_eq!(db.into_transaction_log(), Transaction::wrap(stmts));
assert_eq!(mocker.drain_transaction_log(), Transaction::wrap(stmts));
Ok(()) Ok(())
} }
} }

View File

@ -20,4 +20,4 @@ pub use select::*;
pub use traits::*; pub use traits::*;
pub use update::*; pub use update::*;
pub use crate::executor::{ExecErr, QueryErr}; pub use crate::executor::{ExecErr, InsertResult, QueryErr, UpdateResult};