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