sea_orm/database/
mock.rs

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/// Defines a Mock database suitable for testing
11#[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/// Defines the results obtained from a [MockDatabase]
21#[derive(Clone, Debug, Default)]
22pub struct MockExecResult {
23    /// The last inserted id on auto-increment
24    pub last_insert_id: u64,
25    /// The number of rows affected by the database operation
26    pub rows_affected: u64,
27}
28
29/// Defines the structure of a test Row for the [MockDatabase]
30/// which is just a [BTreeMap]<[String], [Value]>
31#[derive(Clone, Debug)]
32pub struct MockRow {
33    /// The values of the single row
34    pub(crate) values: BTreeMap<String, Value>,
35}
36
37/// A trait to get a [MockRow] from a type useful for testing in the [MockDatabase]
38pub trait IntoMockRow {
39    /// The method to perform this operation
40    fn into_mock_row(self) -> MockRow;
41}
42
43/// Defines a transaction that is has not been committed
44#[derive(Debug)]
45pub struct OpenTransaction {
46    stmts: Vec<Statement>,
47    transaction_depth: usize,
48}
49
50/// Defines a database transaction as it holds a Vec<[Statement]>
51#[derive(Debug, Clone, PartialEq)]
52pub struct Transaction {
53    stmts: Vec<Statement>,
54}
55
56impl MockDatabase {
57    /// Instantiate a mock database with a [DbBackend] to simulate real
58    /// world SQL databases
59    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    /// Create a database connection
70    pub fn into_connection(self) -> DatabaseConnection {
71        DatabaseConnectionType::MockDatabaseConnection(Arc::new(MockDatabaseConnection::new(self)))
72            .into()
73    }
74
75    /// Add some [MockExecResult]s to `exec_results`
76    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    /// Add some Values to `query_results`
85    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    /// Add some [DbErr]s to `exec_results`
99    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    /// Add some [DbErr]s to `query_results`
108    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    /// Get a value from the [MockRow]
216    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    /// An iterator over the keys and values of a mock row
241    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    /// Get the [Value]s from s raw SQL statement depending on the [DatabaseBackend](crate::DatabaseBackend)
330    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    /// Create a Transaction with one statement
342    pub fn one(stmt: Statement) -> Self {
343        Self { stmts: vec![stmt] }
344    }
345
346    /// Create a Transaction with many statements
347    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    /// Wrap each Statement as a single-statement Transaction
357    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    /// Get the list of statements
365    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    use crate::{
430        DbBackend, DbErr, IntoMockRow, MockDatabase, Statement, Transaction, TransactionError,
431        TransactionTrait, entity::*, error::*, tests_cfg::*,
432    };
433    use pretty_assertions::assert_eq;
434
435    #[derive(Debug, PartialEq, Eq)]
436    pub struct MyErr(String);
437
438    impl std::error::Error for MyErr {}
439
440    impl std::fmt::Display for MyErr {
441        fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
442            write!(f, "{}", self.0.as_str())
443        }
444    }
445
446    #[smol_potat::test]
447    async fn test_transaction_1() {
448        let db = MockDatabase::new(DbBackend::Postgres).into_connection();
449
450        db.transaction::<_, (), DbErr>(|txn| {
451            Box::pin(async move {
452                let _1 = cake::Entity::find().one(txn).await;
453                let _2 = fruit::Entity::find().all(txn).await;
454
455                Ok(())
456            })
457        })
458        .await
459        .unwrap();
460
461        let _ = cake::Entity::find().all(&db).await;
462
463        assert_eq!(
464            db.into_transaction_log(),
465            [
466                Transaction::many([
467                    Statement::from_string(DbBackend::Postgres, "BEGIN"),
468                    Statement::from_sql_and_values(
469                        DbBackend::Postgres,
470                        r#"SELECT "cake"."id", "cake"."name" FROM "cake" LIMIT $1"#,
471                        [1u64.into()]
472                    ),
473                    Statement::from_sql_and_values(
474                        DbBackend::Postgres,
475                        r#"SELECT "fruit"."id", "fruit"."name", "fruit"."cake_id" FROM "fruit""#,
476                        []
477                    ),
478                    Statement::from_string(DbBackend::Postgres, "COMMIT"),
479                ]),
480                Transaction::from_sql_and_values(
481                    DbBackend::Postgres,
482                    r#"SELECT "cake"."id", "cake"."name" FROM "cake""#,
483                    []
484                ),
485            ]
486        );
487    }
488
489    #[smol_potat::test]
490    async fn test_transaction_2() {
491        let db = MockDatabase::new(DbBackend::Postgres).into_connection();
492
493        let result = db
494            .transaction::<_, (), MyErr>(|txn| {
495                Box::pin(async move {
496                    let _ = cake::Entity::find().one(txn).await;
497                    Err(MyErr("test".to_owned()))
498                })
499            })
500            .await;
501
502        match result {
503            Err(TransactionError::Transaction(err)) => {
504                assert_eq!(err, MyErr("test".to_owned()))
505            }
506            _ => unreachable!(),
507        }
508
509        assert_eq!(
510            db.into_transaction_log(),
511            [Transaction::many([
512                Statement::from_string(DbBackend::Postgres, "BEGIN"),
513                Statement::from_sql_and_values(
514                    DbBackend::Postgres,
515                    r#"SELECT "cake"."id", "cake"."name" FROM "cake" LIMIT $1"#,
516                    [1u64.into()]
517                ),
518                Statement::from_string(DbBackend::Postgres, "ROLLBACK"),
519            ])]
520        );
521    }
522
523    #[smol_potat::test]
524    async fn test_nested_transaction_1() {
525        let db = MockDatabase::new(DbBackend::Postgres).into_connection();
526
527        db.transaction::<_, (), DbErr>(|txn| {
528            Box::pin(async move {
529                let _ = cake::Entity::find().one(txn).await;
530
531                txn.transaction::<_, (), DbErr>(|txn| {
532                    Box::pin(async move {
533                        let _ = fruit::Entity::find().all(txn).await;
534
535                        Ok(())
536                    })
537                })
538                .await
539                .unwrap();
540
541                Ok(())
542            })
543        })
544        .await
545        .unwrap();
546
547        assert_eq!(
548            db.into_transaction_log(),
549            [Transaction::many([
550                Statement::from_string(DbBackend::Postgres, "BEGIN"),
551                Statement::from_sql_and_values(
552                    DbBackend::Postgres,
553                    r#"SELECT "cake"."id", "cake"."name" FROM "cake" LIMIT $1"#,
554                    [1u64.into()]
555                ),
556                Statement::from_string(DbBackend::Postgres, "SAVEPOINT savepoint_1"),
557                Statement::from_sql_and_values(
558                    DbBackend::Postgres,
559                    r#"SELECT "fruit"."id", "fruit"."name", "fruit"."cake_id" FROM "fruit""#,
560                    []
561                ),
562                Statement::from_string(DbBackend::Postgres, "RELEASE SAVEPOINT savepoint_1"),
563                Statement::from_string(DbBackend::Postgres, "COMMIT"),
564            ]),]
565        );
566    }
567
568    #[smol_potat::test]
569    async fn test_nested_transaction_2() {
570        let db = MockDatabase::new(DbBackend::Postgres).into_connection();
571
572        db.transaction::<_, (), DbErr>(|txn| {
573            Box::pin(async move {
574                let _ = cake::Entity::find().one(txn).await;
575
576                txn.transaction::<_, (), DbErr>(|txn| {
577                    Box::pin(async move {
578                        let _ = fruit::Entity::find().all(txn).await;
579
580                        txn.transaction::<_, (), DbErr>(|txn| {
581                            Box::pin(async move {
582                                let _ = cake::Entity::find().all(txn).await;
583
584                                Ok(())
585                            })
586                        })
587                        .await
588                        .unwrap();
589
590                        Ok(())
591                    })
592                })
593                .await
594                .unwrap();
595
596                Ok(())
597            })
598        })
599        .await
600        .unwrap();
601
602        assert_eq!(
603            db.into_transaction_log(),
604            [Transaction::many([
605                Statement::from_string(DbBackend::Postgres, "BEGIN"),
606                Statement::from_sql_and_values(
607                    DbBackend::Postgres,
608                    r#"SELECT "cake"."id", "cake"."name" FROM "cake" LIMIT $1"#,
609                    [1u64.into()]
610                ),
611                Statement::from_string(DbBackend::Postgres, "SAVEPOINT savepoint_1"),
612                Statement::from_sql_and_values(
613                    DbBackend::Postgres,
614                    r#"SELECT "fruit"."id", "fruit"."name", "fruit"."cake_id" FROM "fruit""#,
615                    []
616                ),
617                Statement::from_string(DbBackend::Postgres, "SAVEPOINT savepoint_2"),
618                Statement::from_sql_and_values(
619                    DbBackend::Postgres,
620                    r#"SELECT "cake"."id", "cake"."name" FROM "cake""#,
621                    []
622                ),
623                Statement::from_string(DbBackend::Postgres, "RELEASE SAVEPOINT savepoint_2"),
624                Statement::from_string(DbBackend::Postgres, "RELEASE SAVEPOINT savepoint_1"),
625                Statement::from_string(DbBackend::Postgres, "COMMIT"),
626            ]),]
627        );
628    }
629
630    #[smol_potat::test]
631    async fn test_stream_1() -> Result<(), DbErr> {
632        use futures_util::TryStreamExt;
633
634        let apple = fruit::Model {
635            id: 1,
636            name: "Apple".to_owned(),
637            cake_id: Some(1),
638        };
639
640        let orange = fruit::Model {
641            id: 2,
642            name: "orange".to_owned(),
643            cake_id: None,
644        };
645
646        let db = MockDatabase::new(DbBackend::Postgres)
647            .append_query_results([[apple.clone(), orange.clone()]])
648            .into_connection();
649
650        let mut stream = fruit::Entity::find().stream(&db).await?;
651
652        assert_eq!(stream.try_next().await?, Some(apple));
653
654        assert_eq!(stream.try_next().await?, Some(orange));
655
656        assert_eq!(stream.try_next().await?, None);
657
658        Ok(())
659    }
660
661    #[smol_potat::test]
662    async fn test_stream_2() -> Result<(), DbErr> {
663        use fruit::Entity as Fruit;
664        use futures_util::TryStreamExt;
665
666        let db = MockDatabase::new(DbBackend::Postgres)
667            .append_query_results([Vec::<fruit::Model>::new()])
668            .into_connection();
669
670        let mut stream = Fruit::find().stream(&db).await?;
671
672        while let Some(item) = stream.try_next().await? {
673            let _item: fruit::ActiveModel = item.into();
674        }
675
676        Ok(())
677    }
678
679    #[smol_potat::test]
680    async fn test_stream_in_transaction() -> Result<(), DbErr> {
681        use futures_util::TryStreamExt;
682
683        let apple = fruit::Model {
684            id: 1,
685            name: "Apple".to_owned(),
686            cake_id: Some(1),
687        };
688
689        let orange = fruit::Model {
690            id: 2,
691            name: "orange".to_owned(),
692            cake_id: None,
693        };
694
695        let db = MockDatabase::new(DbBackend::Postgres)
696            .append_query_results([[apple.clone(), orange.clone()]])
697            .into_connection();
698
699        let txn = db.begin().await?;
700
701        if let Ok(mut stream) = fruit::Entity::find().stream(&txn).await {
702            assert_eq!(stream.try_next().await?, Some(apple));
703
704            assert_eq!(stream.try_next().await?, Some(orange));
705
706            assert_eq!(stream.try_next().await?, None);
707
708            // stream will be dropped end of scope
709        }
710
711        txn.commit().await?;
712
713        Ok(())
714    }
715
716    #[smol_potat::test]
717    async fn test_mocked_join() {
718        let row = (
719            cake::Model {
720                id: 1,
721                name: "Apple Cake".to_owned(),
722            },
723            fruit::Model {
724                id: 2,
725                name: "Apple".to_owned(),
726                cake_id: Some(1),
727            },
728        );
729        let mocked_row = row.into_mock_row();
730
731        let a_id = mocked_row.try_get::<i32, _>("A_id");
732        assert!(a_id.is_ok());
733        assert_eq!(1, a_id.unwrap());
734        let b_id = mocked_row.try_get::<i32, _>("B_id");
735        assert!(b_id.is_ok());
736        assert_eq!(2, b_id.unwrap());
737    }
738
739    #[smol_potat::test]
740    async fn test_find_also_related_1() -> Result<(), DbErr> {
741        let db = MockDatabase::new(DbBackend::Postgres)
742            .append_query_results([[(
743                cake::Model {
744                    id: 1,
745                    name: "Apple Cake".to_owned(),
746                },
747                fruit::Model {
748                    id: 2,
749                    name: "Apple".to_owned(),
750                    cake_id: Some(1),
751                },
752            )]])
753            .into_connection();
754
755        assert_eq!(
756            cake::Entity::find()
757                .find_also_related(fruit::Entity)
758                .all(&db)
759                .await?,
760            [(
761                cake::Model {
762                    id: 1,
763                    name: "Apple Cake".to_owned(),
764                },
765                Some(fruit::Model {
766                    id: 2,
767                    name: "Apple".to_owned(),
768                    cake_id: Some(1),
769                })
770            )]
771        );
772
773        assert_eq!(
774            db.into_transaction_log(),
775            [Transaction::from_sql_and_values(
776                DbBackend::Postgres,
777                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""#,
778                []
779            ),]
780        );
781
782        Ok(())
783    }
784
785    #[cfg(feature = "postgres-array")]
786    #[smol_potat::test]
787    async fn test_postgres_array_1() -> Result<(), DbErr> {
788        mod collection {
789            use crate as sea_orm;
790            use crate::entity::prelude::*;
791
792            #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
793            #[sea_orm(table_name = "collection")]
794            pub struct Model {
795                #[sea_orm(primary_key)]
796                pub id: i32,
797                pub integers: Vec<i32>,
798                pub integers_opt: Option<Vec<i32>>,
799            }
800
801            #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
802            pub enum Relation {}
803
804            impl ActiveModelBehavior for ActiveModel {}
805        }
806
807        let db = MockDatabase::new(DbBackend::Postgres)
808            .append_query_results([[
809                collection::Model {
810                    id: 1,
811                    integers: vec![1, 2, 3],
812                    integers_opt: Some(vec![1, 2, 3]),
813                },
814                collection::Model {
815                    id: 2,
816                    integers: vec![],
817                    integers_opt: Some(vec![]),
818                },
819                collection::Model {
820                    id: 3,
821                    integers: vec![3, 1, 4],
822                    integers_opt: None,
823                },
824            ]])
825            .into_connection();
826
827        assert_eq!(
828            collection::Entity::find().all(&db).await?,
829            [
830                collection::Model {
831                    id: 1,
832                    integers: vec![1, 2, 3],
833                    integers_opt: Some(vec![1, 2, 3]),
834                },
835                collection::Model {
836                    id: 2,
837                    integers: vec![],
838                    integers_opt: Some(vec![]),
839                },
840                collection::Model {
841                    id: 3,
842                    integers: vec![3, 1, 4],
843                    integers_opt: None,
844                },
845            ]
846        );
847
848        assert_eq!(
849            db.into_transaction_log(),
850            [Transaction::from_sql_and_values(
851                DbBackend::Postgres,
852                r#"SELECT "collection"."id", "collection"."integers", "collection"."integers_opt" FROM "collection""#,
853                []
854            ),]
855        );
856
857        Ok(())
858    }
859
860    #[smol_potat::test]
861    async fn test_query_err() {
862        let db = MockDatabase::new(DbBackend::MySql)
863            .append_query_errors([query_err("this is a mock query error")])
864            .into_connection();
865
866        assert_eq!(
867            cake::Entity::find().all(&db).await,
868            Err(query_err("this is a mock query error"))
869        );
870    }
871
872    #[smol_potat::test]
873    async fn test_exec_err() {
874        let db = MockDatabase::new(DbBackend::MySql)
875            .append_exec_errors([exec_err("this is a mock exec error")])
876            .into_connection();
877
878        let model = cake::ActiveModel::new();
879
880        assert_eq!(
881            model.save(&db).await,
882            Err(exec_err("this is a mock exec error"))
883        );
884    }
885}