sea_orm/database/
mock.rs

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/// 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        DatabaseConnection::MockDatabaseConnection(Arc::new(MockDatabaseConnection::new(self)))
72    }
73
74    /// Add some [MockExecResult]s to `exec_results`
75    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    /// Add some Values to `query_results`
84    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    /// Add some [DbErr]s to `exec_results`
98    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    /// Add some [DbErr]s to `query_results`
107    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    /// Get a value from the [MockRow]
215    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    /// An iterator over the keys and values of a mock row
240    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    /// Get the [Value]s from s raw SQL statement depending on the [DatabaseBackend](crate::DatabaseBackend)
329    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    /// Create a Transaction with one statement
341    pub fn one(stmt: Statement) -> Self {
342        Self { stmts: vec![stmt] }
343    }
344
345    /// Create a Transaction with many statements
346    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    /// Wrap each Statement as a single-statement Transaction
356    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    /// Get the list of statements
364    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            // stream will be dropped end of scope
708        }
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}