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