Skip to main content

chopin_orm/
mock.rs

1use crate::{Executor, OrmResult};
2use chopin_pg::Row;
3use std::collections::VecDeque;
4
5/// An in-memory testing stub satisfying the `Executor` trait without PostgreSQL connections.
6///
7/// Queues mock results that are drained in FIFO order as queries are executed.
8pub struct MockExecutor {
9    /// Records all executed queries as `(sql, param_count)` tuples.
10    pub executed_queries: Vec<(String, usize)>,
11    mocked_results: VecDeque<Vec<Row>>,
12}
13
14impl MockExecutor {
15    pub fn new() -> Self {
16        Self {
17            executed_queries: Vec::new(),
18            mocked_results: VecDeque::new(),
19        }
20    }
21
22    /// Enqueues a set of rows to be returned by the next `query()` call.
23    pub fn push_result(&mut self, rows: Vec<Row>) {
24        self.mocked_results.push_back(rows);
25    }
26
27    /// Returns the number of remaining mocked result sets.
28    pub fn remaining_results(&self) -> usize {
29        self.mocked_results.len()
30    }
31
32    /// Clears all recorded queries and remaining mocked results.
33    pub fn reset(&mut self) {
34        self.executed_queries.clear();
35        self.mocked_results.clear();
36    }
37}
38
39impl Default for MockExecutor {
40    fn default() -> Self {
41        Self::new()
42    }
43}
44
45impl Executor for MockExecutor {
46    fn execute(&mut self, query: &str, params: &[&dyn chopin_pg::types::ToSql]) -> OrmResult<u64> {
47        self.executed_queries
48            .push((query.to_string(), params.len()));
49        Ok(1)
50    }
51
52    fn query(
53        &mut self,
54        query: &str,
55        params: &[&dyn chopin_pg::types::ToSql],
56    ) -> OrmResult<Vec<Row>> {
57        self.executed_queries
58            .push((query.to_string(), params.len()));
59        if let Some(rows) = self.mocked_results.pop_front() {
60            Ok(rows)
61        } else {
62            Ok(vec![])
63        }
64    }
65}
66
67/// Constructs a sequence-free structurally valid `chopin_pg::Row` from literal tuple representations.
68///
69/// Useful directly within `MockExecutor::push_result`.
70#[macro_export]
71macro_rules! mock_row {
72    ( $( $name:expr => $val:expr ),* $(,)? ) => {
73        {
74            use chopin_pg::types::ToParam;
75            let mut names = Vec::new();
76            let mut vals: Vec<chopin_pg::PgValue> = Vec::new();
77            $(
78                names.push($name);
79                vals.push($val.to_param());
80            )*
81            chopin_pg::Row::mock(&names, &vals)
82        }
83    };
84}
85
86#[cfg(test)]
87#[allow(dead_code)]
88mod tests {
89    use super::*;
90    use crate as chopin_orm;
91    use crate::{Model, builder::ColumnTrait};
92
93    #[derive(Model, Debug, Clone, PartialEq)]
94    #[model(table_name = "tester")]
95    pub struct Tester {
96        #[model(primary_key)]
97        pub id: i32,
98        pub name: String,
99    }
100    impl crate::Validate for Tester {}
101
102    #[test]
103    fn test_mock_executor() {
104        let mut mock = MockExecutor::new();
105        mock.push_result(vec![
106            mock_row!("id" => 1, "name" => "Alice"),
107            mock_row!("id" => 2, "name" => "Bob"),
108        ]);
109
110        let results = Tester::find()
111            .filter(TesterColumn::id.gt(0))
112            .all(&mut mock)
113            .unwrap();
114
115        assert_eq!(results.len(), 2);
116        assert_eq!(results[0].name, "Alice");
117        assert_eq!(results[1].name, "Bob");
118        assert_eq!(mock.executed_queries.len(), 1);
119        assert!(
120            mock.executed_queries[0]
121                .0
122                .contains("SELECT id, name FROM tester WHERE id > $1")
123        );
124    }
125}