testkit_core/testdb/
transaction.rs

1use async_trait::async_trait;
2
3/// Trait for managing database transactions
4///
5/// This trait allows for common transaction operations like beginning, committing, and
6/// rolling back transactions. It's used by the `with_transaction` operator to provide
7/// automatic transaction management.
8#[async_trait]
9pub trait DBTransactionManager<Tx, Conn>: Send + Sync {
10    /// The error type for transaction operations
11    type Error: Send + Sync;
12
13    /// The transaction type returned by begin_transaction
14    type Tx: DatabaseTransaction<Error = Self::Error> + Send + Sync;
15
16    /// Begin a new transaction
17    async fn begin_transaction(&mut self) -> Result<Self::Tx, Self::Error>;
18
19    /// Commit a transaction
20    async fn commit_transaction(tx: &mut Self::Tx) -> Result<(), Self::Error>;
21
22    /// Rollback a transaction
23    async fn rollback_transaction(tx: &mut Self::Tx) -> Result<(), Self::Error>;
24}
25
26/// Trait for objects that represent database transactions
27///
28/// This trait allows database-specific transaction types to be used
29/// with the transaction management system.
30#[async_trait]
31pub trait DatabaseTransaction: Send + Sync {
32    /// The error type for transaction operations
33    type Error: Send + Sync;
34
35    /// Commit the transaction
36    async fn commit(&mut self) -> Result<(), Self::Error>;
37
38    /// Rollback the transaction
39    async fn rollback(&mut self) -> Result<(), Self::Error>;
40}
41
42// Implementation of TransactionManager for TestDatabaseInstance
43#[cfg(test)]
44pub mod tests {
45    use super::*;
46    use crate::{
47        DatabaseBackend, DatabaseConfig, DatabaseName, DatabasePool, TestDatabaseConnection,
48        TestDatabaseInstance,
49    };
50    use std::fmt::Debug;
51
52    // Mock transaction type for testing
53    #[derive(Debug, Clone, Default)]
54    pub struct MockTransaction {
55        committed: bool,
56        rolled_back: bool,
57    }
58
59    impl MockTransaction {
60        pub fn is_committed(&self) -> bool {
61            self.committed
62        }
63
64        pub fn is_rolled_back(&self) -> bool {
65            self.rolled_back
66        }
67    }
68
69    #[async_trait]
70    impl DatabaseTransaction for MockTransaction {
71        type Error = String;
72
73        async fn commit(&mut self) -> Result<(), Self::Error> {
74            self.committed = true;
75            Ok(())
76        }
77
78        async fn rollback(&mut self) -> Result<(), Self::Error> {
79            self.rolled_back = true;
80            Ok(())
81        }
82    }
83
84    // Mock connection type for testing
85    #[derive(Debug, Clone)]
86    pub struct MockConnection(i32);
87
88    impl MockConnection {
89        pub fn set_value(&mut self, value: i32) {
90            self.0 = value;
91        }
92
93        pub fn get_value(&self) -> i32 {
94            self.0
95        }
96    }
97
98    impl TestDatabaseConnection for MockConnection {
99        fn connection_string(&self) -> String {
100            format!("mock://localhost/test{}", self.0)
101        }
102    }
103
104    // Mock pool for testing
105    #[derive(Debug, Clone)]
106    pub struct MockPool;
107
108    #[async_trait]
109    impl DatabasePool for MockPool {
110        type Connection = MockConnection;
111        type Error = String;
112
113        async fn acquire(&self) -> Result<Self::Connection, Self::Error> {
114            Ok(MockConnection(0))
115        }
116
117        async fn release(&self, _conn: Self::Connection) -> Result<(), Self::Error> {
118            Ok(())
119        }
120
121        fn connection_string(&self) -> String {
122            "mock://localhost/test".to_string()
123        }
124    }
125
126    // Mock backend for testing
127    #[derive(Debug, Clone)]
128    pub struct MockBackend;
129
130    #[async_trait]
131    impl DatabaseBackend for MockBackend {
132        type Connection = MockConnection;
133        type Pool = MockPool;
134        type Error = String;
135
136        async fn new(_config: DatabaseConfig) -> Result<Self, Self::Error> {
137            Ok(MockBackend)
138        }
139
140        async fn connect(&self, _name: &DatabaseName) -> Result<Self::Connection, Self::Error> {
141            Ok(MockConnection(0))
142        }
143
144        async fn connect_with_string(
145            &self,
146            _connection_string: &str,
147        ) -> Result<Self::Connection, Self::Error> {
148            Ok(MockConnection(0))
149        }
150
151        async fn create_pool(
152            &self,
153            _name: &DatabaseName,
154            _config: &DatabaseConfig,
155        ) -> Result<Self::Pool, Self::Error> {
156            Ok(MockPool)
157        }
158
159        async fn create_database(
160            &self,
161            _pool: &Self::Pool,
162            _name: &DatabaseName,
163        ) -> Result<(), Self::Error> {
164            Ok(())
165        }
166
167        fn drop_database(&self, _name: &DatabaseName) -> Result<(), Self::Error> {
168            Ok(())
169        }
170
171        fn connection_string(&self, _name: &DatabaseName) -> String {
172            "mock://localhost/test".to_string()
173        }
174    }
175
176    // Mock implementation of TransactionManager for TestDatabaseInstance with MockBackend
177    #[async_trait]
178    impl DBTransactionManager<MockTransaction, MockConnection> for TestDatabaseInstance<MockBackend> {
179        type Error = String;
180        type Tx = MockTransaction;
181
182        async fn begin_transaction(&mut self) -> Result<Self::Tx, Self::Error> {
183            Ok(MockTransaction::default())
184        }
185
186        async fn commit_transaction(tx: &mut Self::Tx) -> Result<(), Self::Error> {
187            tx.commit().await
188        }
189
190        async fn rollback_transaction(tx: &mut Self::Tx) -> Result<(), Self::Error> {
191            tx.rollback().await
192        }
193    }
194}