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 futures_util::{TryStreamExt, stream::TryNext};
436 use pretty_assertions::assert_eq;
437
438 #[derive(Debug, PartialEq, Eq)]
439 pub struct MyErr(String);
440
441 impl std::error::Error for MyErr {}
442
443 impl std::fmt::Display for MyErr {
444 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
445 write!(f, "{}", self.0.as_str())
446 }
447 }
448
449 #[smol_potat::test]
450 async fn test_transaction_1() {
451 let db = MockDatabase::new(DbBackend::Postgres).into_connection();
452
453 db.transaction::<_, (), DbErr>(|txn| {
454 Box::pin(async move {
455 let _1 = cake::Entity::find().one(txn).await;
456 let _2 = fruit::Entity::find().all(txn).await;
457
458 Ok(())
459 })
460 })
461 .await
462 .unwrap();
463
464 let _ = cake::Entity::find().all(&db).await;
465
466 assert_eq!(
467 db.into_transaction_log(),
468 [
469 Transaction::many([
470 Statement::from_string(DbBackend::Postgres, "BEGIN"),
471 Statement::from_sql_and_values(
472 DbBackend::Postgres,
473 r#"SELECT "cake"."id", "cake"."name" FROM "cake" LIMIT $1"#,
474 [1u64.into()]
475 ),
476 Statement::from_sql_and_values(
477 DbBackend::Postgres,
478 r#"SELECT "fruit"."id", "fruit"."name", "fruit"."cake_id" FROM "fruit""#,
479 []
480 ),
481 Statement::from_string(DbBackend::Postgres, "COMMIT"),
482 ]),
483 Transaction::from_sql_and_values(
484 DbBackend::Postgres,
485 r#"SELECT "cake"."id", "cake"."name" FROM "cake""#,
486 []
487 ),
488 ]
489 );
490 }
491
492 #[smol_potat::test]
493 async fn test_transaction_2() {
494 let db = MockDatabase::new(DbBackend::Postgres).into_connection();
495
496 let result = db
497 .transaction::<_, (), MyErr>(|txn| {
498 Box::pin(async move {
499 let _ = cake::Entity::find().one(txn).await;
500 Err(MyErr("test".to_owned()))
501 })
502 })
503 .await;
504
505 match result {
506 Err(TransactionError::Transaction(err)) => {
507 assert_eq!(err, MyErr("test".to_owned()))
508 }
509 _ => unreachable!(),
510 }
511
512 assert_eq!(
513 db.into_transaction_log(),
514 [Transaction::many([
515 Statement::from_string(DbBackend::Postgres, "BEGIN"),
516 Statement::from_sql_and_values(
517 DbBackend::Postgres,
518 r#"SELECT "cake"."id", "cake"."name" FROM "cake" LIMIT $1"#,
519 [1u64.into()]
520 ),
521 Statement::from_string(DbBackend::Postgres, "ROLLBACK"),
522 ])]
523 );
524 }
525
526 #[smol_potat::test]
527 async fn test_nested_transaction_1() {
528 let db = MockDatabase::new(DbBackend::Postgres).into_connection();
529
530 db.transaction::<_, (), DbErr>(|txn| {
531 Box::pin(async move {
532 let _ = cake::Entity::find().one(txn).await;
533
534 txn.transaction::<_, (), DbErr>(|txn| {
535 Box::pin(async move {
536 let _ = fruit::Entity::find().all(txn).await;
537
538 Ok(())
539 })
540 })
541 .await
542 .unwrap();
543
544 Ok(())
545 })
546 })
547 .await
548 .unwrap();
549
550 assert_eq!(
551 db.into_transaction_log(),
552 [Transaction::many([
553 Statement::from_string(DbBackend::Postgres, "BEGIN"),
554 Statement::from_sql_and_values(
555 DbBackend::Postgres,
556 r#"SELECT "cake"."id", "cake"."name" FROM "cake" LIMIT $1"#,
557 [1u64.into()]
558 ),
559 Statement::from_string(DbBackend::Postgres, "SAVEPOINT savepoint_1"),
560 Statement::from_sql_and_values(
561 DbBackend::Postgres,
562 r#"SELECT "fruit"."id", "fruit"."name", "fruit"."cake_id" FROM "fruit""#,
563 []
564 ),
565 Statement::from_string(DbBackend::Postgres, "RELEASE SAVEPOINT savepoint_1"),
566 Statement::from_string(DbBackend::Postgres, "COMMIT"),
567 ]),]
568 );
569 }
570
571 #[smol_potat::test]
572 async fn test_nested_transaction_2() {
573 let db = MockDatabase::new(DbBackend::Postgres).into_connection();
574
575 db.transaction::<_, (), DbErr>(|txn| {
576 Box::pin(async move {
577 let _ = cake::Entity::find().one(txn).await;
578
579 txn.transaction::<_, (), DbErr>(|txn| {
580 Box::pin(async move {
581 let _ = fruit::Entity::find().all(txn).await;
582
583 txn.transaction::<_, (), DbErr>(|txn| {
584 Box::pin(async move {
585 let _ = cake::Entity::find().all(txn).await;
586
587 Ok(())
588 })
589 })
590 .await
591 .unwrap();
592
593 Ok(())
594 })
595 })
596 .await
597 .unwrap();
598
599 Ok(())
600 })
601 })
602 .await
603 .unwrap();
604
605 assert_eq!(
606 db.into_transaction_log(),
607 [Transaction::many([
608 Statement::from_string(DbBackend::Postgres, "BEGIN"),
609 Statement::from_sql_and_values(
610 DbBackend::Postgres,
611 r#"SELECT "cake"."id", "cake"."name" FROM "cake" LIMIT $1"#,
612 [1u64.into()]
613 ),
614 Statement::from_string(DbBackend::Postgres, "SAVEPOINT savepoint_1"),
615 Statement::from_sql_and_values(
616 DbBackend::Postgres,
617 r#"SELECT "fruit"."id", "fruit"."name", "fruit"."cake_id" FROM "fruit""#,
618 []
619 ),
620 Statement::from_string(DbBackend::Postgres, "SAVEPOINT savepoint_2"),
621 Statement::from_sql_and_values(
622 DbBackend::Postgres,
623 r#"SELECT "cake"."id", "cake"."name" FROM "cake""#,
624 []
625 ),
626 Statement::from_string(DbBackend::Postgres, "RELEASE SAVEPOINT savepoint_2"),
627 Statement::from_string(DbBackend::Postgres, "RELEASE SAVEPOINT savepoint_1"),
628 Statement::from_string(DbBackend::Postgres, "COMMIT"),
629 ]),]
630 );
631 }
632
633 #[smol_potat::test]
634 async fn test_stream_1() -> Result<(), DbErr> {
635 let apple = fruit::Model {
636 id: 1,
637 name: "Apple".to_owned(),
638 cake_id: Some(1),
639 };
640
641 let orange = fruit::Model {
642 id: 2,
643 name: "orange".to_owned(),
644 cake_id: None,
645 };
646
647 let db = MockDatabase::new(DbBackend::Postgres)
648 .append_query_results([[apple.clone(), orange.clone()]])
649 .into_connection();
650
651 let mut stream = fruit::Entity::find().stream(&db).await?;
652
653 assert_eq!(stream.try_next().await?, Some(apple));
654
655 assert_eq!(stream.try_next().await?, Some(orange));
656
657 assert_eq!(stream.try_next().await?, None);
658
659 Ok(())
660 }
661
662 #[smol_potat::test]
663 async fn test_stream_2() -> Result<(), DbErr> {
664 use fruit::Entity as Fruit;
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 let apple = fruit::Model {
681 id: 1,
682 name: "Apple".to_owned(),
683 cake_id: Some(1),
684 };
685
686 let orange = fruit::Model {
687 id: 2,
688 name: "orange".to_owned(),
689 cake_id: None,
690 };
691
692 let db = MockDatabase::new(DbBackend::Postgres)
693 .append_query_results([[apple.clone(), orange.clone()]])
694 .into_connection();
695
696 let txn = db.begin().await?;
697
698 if let Ok(mut stream) = fruit::Entity::find().stream(&txn).await {
699 assert_eq!(stream.try_next().await?, Some(apple));
700
701 assert_eq!(stream.try_next().await?, Some(orange));
702
703 assert_eq!(stream.try_next().await?, None);
704
705 }
707
708 txn.commit().await?;
709
710 Ok(())
711 }
712
713 #[smol_potat::test]
714 async fn test_mocked_join() {
715 let row = (
716 cake::Model {
717 id: 1,
718 name: "Apple Cake".to_owned(),
719 },
720 fruit::Model {
721 id: 2,
722 name: "Apple".to_owned(),
723 cake_id: Some(1),
724 },
725 );
726 let mocked_row = row.into_mock_row();
727
728 let a_id = mocked_row.try_get::<i32, _>("A_id");
729 assert!(a_id.is_ok());
730 assert_eq!(1, a_id.unwrap());
731 let b_id = mocked_row.try_get::<i32, _>("B_id");
732 assert!(b_id.is_ok());
733 assert_eq!(2, b_id.unwrap());
734 }
735
736 #[smol_potat::test]
737 async fn test_find_also_related_1() -> Result<(), DbErr> {
738 let db = MockDatabase::new(DbBackend::Postgres)
739 .append_query_results([[(
740 cake::Model {
741 id: 1,
742 name: "Apple Cake".to_owned(),
743 },
744 fruit::Model {
745 id: 2,
746 name: "Apple".to_owned(),
747 cake_id: Some(1),
748 },
749 )]])
750 .into_connection();
751
752 assert_eq!(
753 cake::Entity::find()
754 .find_also_related(fruit::Entity)
755 .all(&db)
756 .await?,
757 [(
758 cake::Model {
759 id: 1,
760 name: "Apple Cake".to_owned(),
761 },
762 Some(fruit::Model {
763 id: 2,
764 name: "Apple".to_owned(),
765 cake_id: Some(1),
766 })
767 )]
768 );
769
770 assert_eq!(
771 db.into_transaction_log(),
772 [Transaction::from_sql_and_values(
773 DbBackend::Postgres,
774 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""#,
775 []
776 ),]
777 );
778
779 Ok(())
780 }
781
782 #[cfg(feature = "postgres-array")]
783 #[smol_potat::test]
784 async fn test_postgres_array_1() -> Result<(), DbErr> {
785 mod collection {
786 use crate as sea_orm;
787 use crate::entity::prelude::*;
788
789 #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
790 #[sea_orm(table_name = "collection")]
791 pub struct Model {
792 #[sea_orm(primary_key)]
793 pub id: i32,
794 pub integers: Vec<i32>,
795 pub integers_opt: Option<Vec<i32>>,
796 }
797
798 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
799 pub enum Relation {}
800
801 impl ActiveModelBehavior for ActiveModel {}
802 }
803
804 let db = MockDatabase::new(DbBackend::Postgres)
805 .append_query_results([[
806 collection::Model {
807 id: 1,
808 integers: vec![1, 2, 3],
809 integers_opt: Some(vec![1, 2, 3]),
810 },
811 collection::Model {
812 id: 2,
813 integers: vec![],
814 integers_opt: Some(vec![]),
815 },
816 collection::Model {
817 id: 3,
818 integers: vec![3, 1, 4],
819 integers_opt: None,
820 },
821 ]])
822 .into_connection();
823
824 assert_eq!(
825 collection::Entity::find().all(&db).await?,
826 [
827 collection::Model {
828 id: 1,
829 integers: vec![1, 2, 3],
830 integers_opt: Some(vec![1, 2, 3]),
831 },
832 collection::Model {
833 id: 2,
834 integers: vec![],
835 integers_opt: Some(vec![]),
836 },
837 collection::Model {
838 id: 3,
839 integers: vec![3, 1, 4],
840 integers_opt: None,
841 },
842 ]
843 );
844
845 assert_eq!(
846 db.into_transaction_log(),
847 [Transaction::from_sql_and_values(
848 DbBackend::Postgres,
849 r#"SELECT "collection"."id", "collection"."integers", "collection"."integers_opt" FROM "collection""#,
850 []
851 ),]
852 );
853
854 Ok(())
855 }
856
857 #[smol_potat::test]
858 async fn test_query_err() {
859 let db = MockDatabase::new(DbBackend::MySql)
860 .append_query_errors([query_err("this is a mock query error")])
861 .into_connection();
862
863 assert_eq!(
864 cake::Entity::find().all(&db).await,
865 Err(query_err("this is a mock query error"))
866 );
867 }
868
869 #[smol_potat::test]
870 async fn test_exec_err() {
871 let db = MockDatabase::new(DbBackend::MySql)
872 .append_exec_errors([exec_err("this is a mock exec error")])
873 .into_connection();
874
875 let model = cake::ActiveModel::new();
876
877 assert_eq!(
878 model.save(&db).await,
879 Err(exec_err("this is a mock exec error"))
880 );
881 }
882}