1use crate::{
2 DatabaseConnection, DatabaseConnectionType, DbBackend, EntityTrait, ExecResult,
3 ExecResultHolder, Iden, IdenStatic, Iterable, MockDatabaseConnection, MockDatabaseTrait,
4 ModelTrait, QueryResult, QueryResultRow, SelectA, SelectB, Statement, error::*,
5};
6use sea_query::{Value, ValueType, Values};
7use std::{collections::BTreeMap, sync::Arc};
8use tracing::instrument;
9
10#[derive(Debug)]
12pub struct MockDatabase {
13 db_backend: DbBackend,
14 transaction: Option<OpenTransaction>,
15 transaction_log: Vec<Transaction>,
16 exec_results: Vec<Result<MockExecResult, DbErr>>,
17 query_results: Vec<Result<Vec<MockRow>, DbErr>>,
18}
19
20#[derive(Clone, Debug, Default)]
22pub struct MockExecResult {
23 pub last_insert_id: u64,
25 pub rows_affected: u64,
27}
28
29#[derive(Clone, Debug)]
32pub struct MockRow {
33 pub(crate) values: BTreeMap<String, Value>,
35}
36
37pub trait IntoMockRow {
39 fn into_mock_row(self) -> MockRow;
41}
42
43#[derive(Debug)]
45pub struct OpenTransaction {
46 stmts: Vec<Statement>,
47 transaction_depth: usize,
48}
49
50#[derive(Debug, Clone, PartialEq)]
52pub struct Transaction {
53 stmts: Vec<Statement>,
54}
55
56impl MockDatabase {
57 pub fn new(db_backend: DbBackend) -> Self {
60 Self {
61 db_backend,
62 transaction: None,
63 transaction_log: Vec::new(),
64 exec_results: Vec::new(),
65 query_results: Vec::new(),
66 }
67 }
68
69 pub fn into_connection(self) -> DatabaseConnection {
71 DatabaseConnectionType::MockDatabaseConnection(Arc::new(MockDatabaseConnection::new(self)))
72 .into()
73 }
74
75 pub fn append_exec_results<I>(mut self, vec: I) -> Self
77 where
78 I: IntoIterator<Item = MockExecResult>,
79 {
80 self.exec_results.extend(vec.into_iter().map(Result::Ok));
81 self
82 }
83
84 pub fn append_query_results<T, I, II>(mut self, vec: II) -> Self
86 where
87 T: IntoMockRow,
88 I: IntoIterator<Item = T>,
89 II: IntoIterator<Item = I>,
90 {
91 for row in vec.into_iter() {
92 let row = row.into_iter().map(|vec| Ok(vec.into_mock_row())).collect();
93 self.query_results.push(row);
94 }
95 self
96 }
97
98 pub fn append_exec_errors<I>(mut self, vec: I) -> Self
100 where
101 I: IntoIterator<Item = DbErr>,
102 {
103 self.exec_results.extend(vec.into_iter().map(Result::Err));
104 self
105 }
106
107 pub fn append_query_errors<I>(mut self, vec: I) -> Self
109 where
110 I: IntoIterator<Item = DbErr>,
111 {
112 self.query_results.extend(vec.into_iter().map(Result::Err));
113 self
114 }
115}
116
117impl MockDatabaseTrait for MockDatabase {
118 #[instrument(level = "trace")]
119 fn execute(&mut self, counter: usize, statement: Statement) -> Result<ExecResult, DbErr> {
120 if let Some(transaction) = &mut self.transaction {
121 transaction.push(statement);
122 } else {
123 self.transaction_log.push(Transaction::one(statement));
124 }
125 if counter < self.exec_results.len() {
126 match std::mem::replace(
127 &mut self.exec_results[counter],
128 Err(exec_err("this value has been consumed already")),
129 ) {
130 Ok(result) => Ok(ExecResult {
131 result: ExecResultHolder::Mock(result),
132 }),
133 Err(err) => Err(err),
134 }
135 } else {
136 Err(exec_err("`exec_results` buffer is empty"))
137 }
138 }
139
140 #[instrument(level = "trace")]
141 fn query(&mut self, counter: usize, statement: Statement) -> Result<Vec<QueryResult>, DbErr> {
142 if let Some(transaction) = &mut self.transaction {
143 transaction.push(statement);
144 } else {
145 self.transaction_log.push(Transaction::one(statement));
146 }
147 if counter < self.query_results.len() {
148 match std::mem::replace(
149 &mut self.query_results[counter],
150 Err(query_err("this value has been consumed already")),
151 ) {
152 Ok(result) => Ok(result
153 .into_iter()
154 .map(|row| QueryResult {
155 row: QueryResultRow::Mock(row),
156 })
157 .collect()),
158 Err(err) => Err(err),
159 }
160 } else {
161 Err(query_err("`query_results` buffer is empty."))
162 }
163 }
164
165 #[instrument(level = "trace")]
166 fn begin(&mut self) {
167 match self.transaction.as_mut() {
168 Some(transaction) => transaction.begin_nested(self.db_backend),
169 None => self.transaction = Some(OpenTransaction::init()),
170 }
171 }
172
173 #[instrument(level = "trace")]
174 fn commit(&mut self) {
175 match self.transaction.as_mut() {
176 Some(transaction) => {
177 if transaction.commit(self.db_backend) {
178 if let Some(transaction) = self.transaction.take() {
179 self.transaction_log.push(transaction.into_transaction());
180 }
181 }
182 }
183 None => panic!("There is no open transaction to commit"),
184 }
185 }
186
187 #[instrument(level = "trace")]
188 fn rollback(&mut self) {
189 match self.transaction.as_mut() {
190 Some(transaction) => {
191 if transaction.rollback(self.db_backend) {
192 if let Some(transaction) = self.transaction.take() {
193 self.transaction_log.push(transaction.into_transaction());
194 }
195 }
196 }
197 None => panic!("There is no open transaction to rollback"),
198 }
199 }
200
201 fn drain_transaction_log(&mut self) -> Vec<Transaction> {
202 std::mem::take(&mut self.transaction_log)
203 }
204
205 fn get_database_backend(&self) -> DbBackend {
206 self.db_backend
207 }
208
209 fn ping(&self) -> Result<(), DbErr> {
210 Ok(())
211 }
212}
213
214impl MockRow {
215 pub fn try_get<T, I: crate::ColIdx>(&self, index: I) -> Result<T, DbErr>
217 where
218 T: ValueType,
219 {
220 if let Some(index) = index.as_str() {
221 T::try_from(
222 self.values
223 .get(index)
224 .ok_or_else(|| query_err(format!("No column for ColIdx {index:?}")))?
225 .clone(),
226 )
227 .map_err(type_err)
228 } else if let Some(index) = index.as_usize() {
229 let (_, value) = self
230 .values
231 .iter()
232 .nth(*index)
233 .ok_or_else(|| query_err(format!("Column at index {index} not found")))?;
234 T::try_from(value.clone()).map_err(type_err)
235 } else {
236 unreachable!("Missing ColIdx implementation for MockRow");
237 }
238 }
239
240 pub fn into_column_value_tuples(self) -> impl Iterator<Item = (String, Value)> {
242 self.values.into_iter()
243 }
244}
245
246impl IntoMockRow for MockRow {
247 fn into_mock_row(self) -> MockRow {
248 self
249 }
250}
251
252impl<M> IntoMockRow for M
253where
254 M: ModelTrait,
255{
256 fn into_mock_row(self) -> MockRow {
257 let mut values = BTreeMap::new();
258 for col in <<M::Entity as EntityTrait>::Column>::iter() {
259 values.insert(col.to_string(), self.get(col));
260 }
261 MockRow { values }
262 }
263}
264
265impl<M, N> IntoMockRow for (M, N)
266where
267 M: ModelTrait,
268 N: ModelTrait,
269{
270 fn into_mock_row(self) -> MockRow {
271 let mut mapped_join = BTreeMap::new();
272
273 for column in <<M as ModelTrait>::Entity as EntityTrait>::Column::iter() {
274 mapped_join.insert(
275 format!("{}{}", SelectA.as_str(), column.as_str()),
276 self.0.get(column),
277 );
278 }
279 for column in <<N as ModelTrait>::Entity as EntityTrait>::Column::iter() {
280 mapped_join.insert(
281 format!("{}{}", SelectB.as_str(), column.as_str()),
282 self.1.get(column),
283 );
284 }
285
286 mapped_join.into_mock_row()
287 }
288}
289
290impl<M, N> IntoMockRow for (M, Option<N>)
291where
292 M: ModelTrait,
293 N: ModelTrait,
294{
295 fn into_mock_row(self) -> MockRow {
296 let mut mapped_join = BTreeMap::new();
297
298 for column in <<M as ModelTrait>::Entity as EntityTrait>::Column::iter() {
299 mapped_join.insert(
300 format!("{}{}", SelectA.as_str(), column.as_str()),
301 self.0.get(column),
302 );
303 }
304 if let Some(b_entity) = self.1 {
305 for column in <<N as ModelTrait>::Entity as EntityTrait>::Column::iter() {
306 mapped_join.insert(
307 format!("{}{}", SelectB.as_str(), column.as_str()),
308 b_entity.get(column),
309 );
310 }
311 }
312
313 mapped_join.into_mock_row()
314 }
315}
316
317impl<T> IntoMockRow for BTreeMap<T, Value>
318where
319 T: Into<String>,
320{
321 fn into_mock_row(self) -> MockRow {
322 MockRow {
323 values: self.into_iter().map(|(k, v)| (k.into(), v)).collect(),
324 }
325 }
326}
327
328impl Transaction {
329 pub fn from_sql_and_values<I, T>(db_backend: DbBackend, sql: T, values: I) -> Self
331 where
332 I: IntoIterator<Item = Value>,
333 T: Into<String>,
334 {
335 Self::one(Statement::from_string_values_tuple(
336 db_backend,
337 (sql, Values(values.into_iter().collect())),
338 ))
339 }
340
341 pub fn one(stmt: Statement) -> Self {
343 Self { stmts: vec![stmt] }
344 }
345
346 pub fn many<I>(stmts: I) -> Self
348 where
349 I: IntoIterator<Item = Statement>,
350 {
351 Self {
352 stmts: stmts.into_iter().collect(),
353 }
354 }
355
356 pub fn wrap<I>(stmts: I) -> Vec<Self>
358 where
359 I: IntoIterator<Item = Statement>,
360 {
361 stmts.into_iter().map(Self::one).collect()
362 }
363
364 pub fn statements(&self) -> &[Statement] {
366 &self.stmts
367 }
368}
369
370impl OpenTransaction {
371 fn init() -> Self {
372 Self {
373 stmts: vec![Statement::from_string(DbBackend::Postgres, "BEGIN")],
374 transaction_depth: 0,
375 }
376 }
377
378 fn begin_nested(&mut self, db_backend: DbBackend) {
379 self.transaction_depth += 1;
380 self.push(Statement::from_string(
381 db_backend,
382 format!("SAVEPOINT savepoint_{}", self.transaction_depth),
383 ));
384 }
385
386 fn commit(&mut self, db_backend: DbBackend) -> bool {
387 if self.transaction_depth == 0 {
388 self.push(Statement::from_string(db_backend, "COMMIT"));
389 true
390 } else {
391 self.push(Statement::from_string(
392 db_backend,
393 format!("RELEASE SAVEPOINT savepoint_{}", self.transaction_depth),
394 ));
395 self.transaction_depth -= 1;
396 false
397 }
398 }
399
400 fn rollback(&mut self, db_backend: DbBackend) -> bool {
401 if self.transaction_depth == 0 {
402 self.push(Statement::from_string(db_backend, "ROLLBACK"));
403 true
404 } else {
405 self.push(Statement::from_string(
406 db_backend,
407 format!("ROLLBACK TO SAVEPOINT savepoint_{}", self.transaction_depth),
408 ));
409 self.transaction_depth -= 1;
410 false
411 }
412 }
413
414 fn push(&mut self, stmt: Statement) {
415 self.stmts.push(stmt);
416 }
417
418 fn into_transaction(self) -> Transaction {
419 match self.transaction_depth {
420 0 => Transaction { stmts: self.stmts },
421 _ => panic!("There is uncommitted nested transaction"),
422 }
423 }
424}
425
426#[cfg(test)]
427#[cfg(feature = "mock")]
428mod tests {
429 #[cfg(feature = "sync")]
430 use crate::util::StreamShim;
431 use crate::{
432 DbBackend, DbErr, IntoMockRow, MockDatabase, Statement, Transaction, TransactionError,
433 TransactionTrait, entity::*, error::*, tests_cfg::*,
434 };
435 use pretty_assertions::assert_eq;
436
437 #[derive(Debug, PartialEq, Eq)]
438 pub struct MyErr(String);
439
440 impl std::error::Error for MyErr {}
441
442 impl std::fmt::Display for MyErr {
443 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
444 write!(f, "{}", self.0.as_str())
445 }
446 }
447
448 #[test]
449 fn test_transaction_1() {
450 let db = MockDatabase::new(DbBackend::Postgres).into_connection();
451
452 db.transaction::<_, (), DbErr>(|txn| {
453 ({
454 let _1 = cake::Entity::find().one(txn);
455 let _2 = fruit::Entity::find().all(txn);
456
457 Ok(())
458 })
459 })
460 .unwrap();
461
462 let _ = cake::Entity::find().all(&db);
463
464 assert_eq!(
465 db.into_transaction_log(),
466 [
467 Transaction::many([
468 Statement::from_string(DbBackend::Postgres, "BEGIN"),
469 Statement::from_sql_and_values(
470 DbBackend::Postgres,
471 r#"SELECT "cake"."id", "cake"."name" FROM "cake" LIMIT $1"#,
472 [1u64.into()]
473 ),
474 Statement::from_sql_and_values(
475 DbBackend::Postgres,
476 r#"SELECT "fruit"."id", "fruit"."name", "fruit"."cake_id" FROM "fruit""#,
477 []
478 ),
479 Statement::from_string(DbBackend::Postgres, "COMMIT"),
480 ]),
481 Transaction::from_sql_and_values(
482 DbBackend::Postgres,
483 r#"SELECT "cake"."id", "cake"."name" FROM "cake""#,
484 []
485 ),
486 ]
487 );
488 }
489
490 #[test]
491 fn test_transaction_2() {
492 let db = MockDatabase::new(DbBackend::Postgres).into_connection();
493
494 let result = db.transaction::<_, (), MyErr>(|txn| {
495 ({
496 let _ = cake::Entity::find().one(txn);
497 Err(MyErr("test".to_owned()))
498 })
499 });
500
501 match result {
502 Err(TransactionError::Transaction(err)) => {
503 assert_eq!(err, MyErr("test".to_owned()))
504 }
505 _ => unreachable!(),
506 }
507
508 assert_eq!(
509 db.into_transaction_log(),
510 [Transaction::many([
511 Statement::from_string(DbBackend::Postgres, "BEGIN"),
512 Statement::from_sql_and_values(
513 DbBackend::Postgres,
514 r#"SELECT "cake"."id", "cake"."name" FROM "cake" LIMIT $1"#,
515 [1u64.into()]
516 ),
517 Statement::from_string(DbBackend::Postgres, "ROLLBACK"),
518 ])]
519 );
520 }
521
522 #[test]
523 fn test_nested_transaction_1() {
524 let db = MockDatabase::new(DbBackend::Postgres).into_connection();
525
526 db.transaction::<_, (), DbErr>(|txn| {
527 ({
528 let _ = cake::Entity::find().one(txn);
529
530 txn.transaction::<_, (), DbErr>(|txn| {
531 ({
532 let _ = fruit::Entity::find().all(txn);
533
534 Ok(())
535 })
536 })
537 .unwrap();
538
539 Ok(())
540 })
541 })
542 .unwrap();
543
544 assert_eq!(
545 db.into_transaction_log(),
546 [Transaction::many([
547 Statement::from_string(DbBackend::Postgres, "BEGIN"),
548 Statement::from_sql_and_values(
549 DbBackend::Postgres,
550 r#"SELECT "cake"."id", "cake"."name" FROM "cake" LIMIT $1"#,
551 [1u64.into()]
552 ),
553 Statement::from_string(DbBackend::Postgres, "SAVEPOINT savepoint_1"),
554 Statement::from_sql_and_values(
555 DbBackend::Postgres,
556 r#"SELECT "fruit"."id", "fruit"."name", "fruit"."cake_id" FROM "fruit""#,
557 []
558 ),
559 Statement::from_string(DbBackend::Postgres, "RELEASE SAVEPOINT savepoint_1"),
560 Statement::from_string(DbBackend::Postgres, "COMMIT"),
561 ]),]
562 );
563 }
564
565 #[test]
566 fn test_nested_transaction_2() {
567 let db = MockDatabase::new(DbBackend::Postgres).into_connection();
568
569 db.transaction::<_, (), DbErr>(|txn| {
570 ({
571 let _ = cake::Entity::find().one(txn);
572
573 txn.transaction::<_, (), DbErr>(|txn| {
574 ({
575 let _ = fruit::Entity::find().all(txn);
576
577 txn.transaction::<_, (), DbErr>(|txn| {
578 ({
579 let _ = cake::Entity::find().all(txn);
580
581 Ok(())
582 })
583 })
584 .unwrap();
585
586 Ok(())
587 })
588 })
589 .unwrap();
590
591 Ok(())
592 })
593 })
594 .unwrap();
595
596 assert_eq!(
597 db.into_transaction_log(),
598 [Transaction::many([
599 Statement::from_string(DbBackend::Postgres, "BEGIN"),
600 Statement::from_sql_and_values(
601 DbBackend::Postgres,
602 r#"SELECT "cake"."id", "cake"."name" FROM "cake" LIMIT $1"#,
603 [1u64.into()]
604 ),
605 Statement::from_string(DbBackend::Postgres, "SAVEPOINT savepoint_1"),
606 Statement::from_sql_and_values(
607 DbBackend::Postgres,
608 r#"SELECT "fruit"."id", "fruit"."name", "fruit"."cake_id" FROM "fruit""#,
609 []
610 ),
611 Statement::from_string(DbBackend::Postgres, "SAVEPOINT savepoint_2"),
612 Statement::from_sql_and_values(
613 DbBackend::Postgres,
614 r#"SELECT "cake"."id", "cake"."name" FROM "cake""#,
615 []
616 ),
617 Statement::from_string(DbBackend::Postgres, "RELEASE SAVEPOINT savepoint_2"),
618 Statement::from_string(DbBackend::Postgres, "RELEASE SAVEPOINT savepoint_1"),
619 Statement::from_string(DbBackend::Postgres, "COMMIT"),
620 ]),]
621 );
622 }
623
624 #[test]
625 fn test_stream_1() -> Result<(), DbErr> {
626 let apple = fruit::Model {
627 id: 1,
628 name: "Apple".to_owned(),
629 cake_id: Some(1),
630 };
631
632 let orange = fruit::Model {
633 id: 2,
634 name: "orange".to_owned(),
635 cake_id: None,
636 };
637
638 let db = MockDatabase::new(DbBackend::Postgres)
639 .append_query_results([[apple.clone(), orange.clone()]])
640 .into_connection();
641
642 let mut stream = fruit::Entity::find().stream(&db)?;
643
644 assert_eq!(stream.try_next()?, Some(apple));
645
646 assert_eq!(stream.try_next()?, Some(orange));
647
648 assert_eq!(stream.try_next()?, None);
649
650 Ok(())
651 }
652
653 #[test]
654 fn test_stream_2() -> Result<(), DbErr> {
655 use fruit::Entity as Fruit;
656 let db = MockDatabase::new(DbBackend::Postgres)
657 .append_query_results([Vec::<fruit::Model>::new()])
658 .into_connection();
659
660 let mut stream = Fruit::find().stream(&db)?;
661
662 while let Some(item) = stream.try_next()? {
663 let _item: fruit::ActiveModel = item.into();
664 }
665
666 Ok(())
667 }
668
669 #[test]
670 fn test_stream_in_transaction() -> Result<(), DbErr> {
671 let apple = fruit::Model {
672 id: 1,
673 name: "Apple".to_owned(),
674 cake_id: Some(1),
675 };
676
677 let orange = fruit::Model {
678 id: 2,
679 name: "orange".to_owned(),
680 cake_id: None,
681 };
682
683 let db = MockDatabase::new(DbBackend::Postgres)
684 .append_query_results([[apple.clone(), orange.clone()]])
685 .into_connection();
686
687 let txn = db.begin()?;
688
689 if let Ok(mut stream) = fruit::Entity::find().stream(&txn) {
690 assert_eq!(stream.try_next()?, Some(apple));
691
692 assert_eq!(stream.try_next()?, Some(orange));
693
694 assert_eq!(stream.try_next()?, None);
695
696 }
698
699 txn.commit()?;
700
701 Ok(())
702 }
703
704 #[test]
705 fn test_mocked_join() {
706 let row = (
707 cake::Model {
708 id: 1,
709 name: "Apple Cake".to_owned(),
710 },
711 fruit::Model {
712 id: 2,
713 name: "Apple".to_owned(),
714 cake_id: Some(1),
715 },
716 );
717 let mocked_row = row.into_mock_row();
718
719 let a_id = mocked_row.try_get::<i32, _>("A_id");
720 assert!(a_id.is_ok());
721 assert_eq!(1, a_id.unwrap());
722 let b_id = mocked_row.try_get::<i32, _>("B_id");
723 assert!(b_id.is_ok());
724 assert_eq!(2, b_id.unwrap());
725 }
726
727 #[test]
728 fn test_find_also_related_1() -> Result<(), DbErr> {
729 let db = MockDatabase::new(DbBackend::Postgres)
730 .append_query_results([[(
731 cake::Model {
732 id: 1,
733 name: "Apple Cake".to_owned(),
734 },
735 fruit::Model {
736 id: 2,
737 name: "Apple".to_owned(),
738 cake_id: Some(1),
739 },
740 )]])
741 .into_connection();
742
743 assert_eq!(
744 cake::Entity::find()
745 .find_also_related(fruit::Entity)
746 .all(&db)?,
747 [(
748 cake::Model {
749 id: 1,
750 name: "Apple Cake".to_owned(),
751 },
752 Some(fruit::Model {
753 id: 2,
754 name: "Apple".to_owned(),
755 cake_id: Some(1),
756 })
757 )]
758 );
759
760 assert_eq!(
761 db.into_transaction_log(),
762 [Transaction::from_sql_and_values(
763 DbBackend::Postgres,
764 r#"SELECT "cake"."id" AS "A_id", "cake"."name" AS "A_name", "fruit"."id" AS "B_id", "fruit"."name" AS "B_name", "fruit"."cake_id" AS "B_cake_id" FROM "cake" LEFT JOIN "fruit" ON "cake"."id" = "fruit"."cake_id""#,
765 []
766 ),]
767 );
768
769 Ok(())
770 }
771
772 #[cfg(feature = "postgres-array")]
773 #[test]
774 fn test_postgres_array_1() -> Result<(), DbErr> {
775 mod collection {
776 use crate as sea_orm;
777 use crate::entity::prelude::*;
778
779 #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
780 #[sea_orm(table_name = "collection")]
781 pub struct Model {
782 #[sea_orm(primary_key)]
783 pub id: i32,
784 pub integers: Vec<i32>,
785 pub integers_opt: Option<Vec<Option<i32>>>,
786 }
787
788 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
789 pub enum Relation {}
790
791 impl ActiveModelBehavior for ActiveModel {}
792 }
793
794 let db = MockDatabase::new(DbBackend::Postgres)
795 .append_query_results([[
796 collection::Model {
797 id: 1,
798 integers: vec![1, 2, 3],
799 integers_opt: Some(vec![1, 2, 3].into_iter().map(Some).collect()),
800 },
801 collection::Model {
802 id: 2,
803 integers: vec![],
804 integers_opt: Some(vec![]),
805 },
806 collection::Model {
807 id: 3,
808 integers: vec![3, 1, 4],
809 integers_opt: None,
810 },
811 ]])
812 .into_connection();
813
814 assert_eq!(
815 collection::Entity::find().all(&db)?,
816 [
817 collection::Model {
818 id: 1,
819 integers: vec![1, 2, 3],
820 integers_opt: Some(vec![Some(1), Some(2), Some(3)]),
821 },
822 collection::Model {
823 id: 2,
824 integers: vec![],
825 integers_opt: Some(vec![]),
826 },
827 collection::Model {
828 id: 3,
829 integers: vec![3, 1, 4],
830 integers_opt: None,
831 },
832 ]
833 );
834
835 assert_eq!(
836 db.into_transaction_log(),
837 [Transaction::from_sql_and_values(
838 DbBackend::Postgres,
839 r#"SELECT "collection"."id", "collection"."integers", "collection"."integers_opt" FROM "collection""#,
840 []
841 ),]
842 );
843
844 Ok(())
845 }
846
847 #[test]
848 fn test_query_err() {
849 let db = MockDatabase::new(DbBackend::MySql)
850 .append_query_errors([query_err("this is a mock query error")])
851 .into_connection();
852
853 assert_eq!(
854 cake::Entity::find().all(&db),
855 Err(query_err("this is a mock query error"))
856 );
857 }
858
859 #[test]
860 fn test_exec_err() {
861 let db = MockDatabase::new(DbBackend::MySql)
862 .append_exec_errors([exec_err("this is a mock exec error")])
863 .into_connection();
864
865 let model = cake::ActiveModel::new();
866
867 assert_eq!(model.save(&db), Err(exec_err("this is a mock exec error")));
868 }
869}