openzeppelin_relayer/repositories/signer/
mod.rs1mod signer_in_memory;
29mod signer_redis;
30
31pub use signer_in_memory::*;
32pub use signer_redis::*;
33
34use crate::{
35 models::{RepositoryError, SignerRepoModel},
36 repositories::{PaginatedResult, PaginationQuery, Repository},
37};
38use async_trait::async_trait;
39use redis::aio::ConnectionManager;
40use std::sync::Arc;
41
42#[derive(Debug, Clone)]
44pub enum SignerRepositoryStorage {
45 InMemory(InMemorySignerRepository),
46 Redis(RedisSignerRepository),
47}
48
49impl SignerRepositoryStorage {
50 pub fn new_in_memory() -> Self {
51 Self::InMemory(InMemorySignerRepository::new())
52 }
53
54 pub fn new_redis(
55 connection_manager: Arc<ConnectionManager>,
56 key_prefix: String,
57 ) -> Result<Self, RepositoryError> {
58 let redis_repo = RedisSignerRepository::new(connection_manager, key_prefix)?;
59 Ok(Self::Redis(redis_repo))
60 }
61}
62
63#[async_trait]
64impl Repository<SignerRepoModel, String> for SignerRepositoryStorage {
65 async fn create(&self, entity: SignerRepoModel) -> Result<SignerRepoModel, RepositoryError> {
66 match self {
67 SignerRepositoryStorage::InMemory(repo) => repo.create(entity).await,
68 SignerRepositoryStorage::Redis(repo) => repo.create(entity).await,
69 }
70 }
71
72 async fn get_by_id(&self, id: String) -> Result<SignerRepoModel, RepositoryError> {
73 match self {
74 SignerRepositoryStorage::InMemory(repo) => repo.get_by_id(id).await,
75 SignerRepositoryStorage::Redis(repo) => repo.get_by_id(id).await,
76 }
77 }
78
79 async fn list_all(&self) -> Result<Vec<SignerRepoModel>, RepositoryError> {
80 match self {
81 SignerRepositoryStorage::InMemory(repo) => repo.list_all().await,
82 SignerRepositoryStorage::Redis(repo) => repo.list_all().await,
83 }
84 }
85
86 async fn list_paginated(
87 &self,
88 query: PaginationQuery,
89 ) -> Result<PaginatedResult<SignerRepoModel>, RepositoryError> {
90 match self {
91 SignerRepositoryStorage::InMemory(repo) => repo.list_paginated(query).await,
92 SignerRepositoryStorage::Redis(repo) => repo.list_paginated(query).await,
93 }
94 }
95
96 async fn update(
97 &self,
98 id: String,
99 entity: SignerRepoModel,
100 ) -> Result<SignerRepoModel, RepositoryError> {
101 match self {
102 SignerRepositoryStorage::InMemory(repo) => repo.update(id, entity).await,
103 SignerRepositoryStorage::Redis(repo) => repo.update(id, entity).await,
104 }
105 }
106
107 async fn delete_by_id(&self, id: String) -> Result<(), RepositoryError> {
108 match self {
109 SignerRepositoryStorage::InMemory(repo) => repo.delete_by_id(id).await,
110 SignerRepositoryStorage::Redis(repo) => repo.delete_by_id(id).await,
111 }
112 }
113
114 async fn count(&self) -> Result<usize, RepositoryError> {
115 match self {
116 SignerRepositoryStorage::InMemory(repo) => repo.count().await,
117 SignerRepositoryStorage::Redis(repo) => repo.count().await,
118 }
119 }
120
121 async fn has_entries(&self) -> Result<bool, RepositoryError> {
122 match self {
123 SignerRepositoryStorage::InMemory(repo) => repo.has_entries().await,
124 SignerRepositoryStorage::Redis(repo) => repo.has_entries().await,
125 }
126 }
127
128 async fn drop_all_entries(&self) -> Result<(), RepositoryError> {
129 match self {
130 SignerRepositoryStorage::InMemory(repo) => repo.drop_all_entries().await,
131 SignerRepositoryStorage::Redis(repo) => repo.drop_all_entries().await,
132 }
133 }
134}
135
136#[cfg(test)]
137mod tests {
138 use super::*;
139 use crate::models::{LocalSignerConfigStorage, SignerConfigStorage};
140 use secrets::SecretVec;
141
142 fn create_local_signer(id: String) -> SignerRepoModel {
143 SignerRepoModel {
144 id: id.clone(),
145 config: SignerConfigStorage::Local(LocalSignerConfigStorage {
146 raw_key: SecretVec::new(32, |v| v.copy_from_slice(&[1; 32])),
147 }),
148 }
149 }
150
151 #[actix_web::test]
152 async fn test_in_memory_impl_creation() {
153 let impl_repo = SignerRepositoryStorage::new_in_memory();
154 match impl_repo {
155 SignerRepositoryStorage::InMemory(_) => (),
156 _ => panic!("Expected InMemory variant"),
157 }
158 }
159
160 #[actix_web::test]
161 async fn test_in_memory_impl_operations() {
162 let impl_repo = SignerRepositoryStorage::new_in_memory();
163 let signer = create_local_signer("test-signer".to_string());
164
165 let created = impl_repo.create(signer.clone()).await.unwrap();
167 assert_eq!(created.id, signer.id);
168
169 let retrieved = impl_repo
171 .get_by_id("test-signer".to_string())
172 .await
173 .unwrap();
174 assert_eq!(retrieved.id, signer.id);
175
176 let count = impl_repo.count().await.unwrap();
178 assert!(count >= 1);
179
180 let all_signers = impl_repo.list_all().await.unwrap();
182 assert!(!all_signers.is_empty());
183
184 let query = PaginationQuery {
186 page: 1,
187 per_page: 10,
188 };
189 let paginated = impl_repo.list_paginated(query).await.unwrap();
190 assert!(!paginated.items.is_empty());
191 }
192
193 #[actix_web::test]
194 async fn test_impl_error_handling() {
195 let impl_repo = SignerRepositoryStorage::new_in_memory();
196
197 let result = impl_repo.get_by_id("non-existent".to_string()).await;
199 assert!(result.is_err());
200 assert!(matches!(result.unwrap_err(), RepositoryError::NotFound(_)));
201 }
202
203 #[actix_web::test]
204 async fn test_impl_debug() {
205 let impl_repo = SignerRepositoryStorage::new_in_memory();
206 let debug_string = format!("{:?}", impl_repo);
207 assert!(debug_string.contains("InMemory"));
208 }
209
210 #[actix_web::test]
211 async fn test_duplicate_creation_error() {
212 let impl_repo = SignerRepositoryStorage::new_in_memory();
213 let signer = create_local_signer("duplicate-test".to_string());
214
215 impl_repo.create(signer.clone()).await.unwrap();
217
218 let result = impl_repo.create(signer).await;
220 assert!(result.is_err());
221 assert!(matches!(
222 result.unwrap_err(),
223 RepositoryError::ConstraintViolation(_)
224 ));
225 }
226
227 #[actix_web::test]
228 async fn test_update_operations() {
229 let impl_repo = SignerRepositoryStorage::new_in_memory();
230 let signer = create_local_signer("update-test".to_string());
231
232 impl_repo.create(signer.clone()).await.unwrap();
234
235 let updated_signer = SignerRepoModel {
237 id: "update-test".to_string(),
238 config: SignerConfigStorage::Local(LocalSignerConfigStorage {
239 raw_key: SecretVec::new(32, |v| v.copy_from_slice(&[2; 32])),
240 }),
241 };
242
243 let result = impl_repo
244 .update("update-test".to_string(), updated_signer)
245 .await;
246 assert!(result.is_ok());
247
248 let non_existent_signer = SignerRepoModel {
250 id: "non-existent".to_string(),
251 config: SignerConfigStorage::Local(LocalSignerConfigStorage {
252 raw_key: SecretVec::new(32, |v| v.copy_from_slice(&[3; 32])),
253 }),
254 };
255
256 let result = impl_repo
257 .update("non-existent".to_string(), non_existent_signer)
258 .await;
259 assert!(result.is_err());
260 assert!(matches!(result.unwrap_err(), RepositoryError::NotFound(_)));
261 }
262
263 #[actix_web::test]
264 async fn test_delete_operations() {
265 let impl_repo = SignerRepositoryStorage::new_in_memory();
266 let signer = create_local_signer("delete-test".to_string());
267
268 impl_repo.create(signer).await.unwrap();
270
271 let result = impl_repo.delete_by_id("delete-test".to_string()).await;
273 assert!(result.is_ok());
274
275 let get_result = impl_repo.get_by_id("delete-test".to_string()).await;
277 assert!(get_result.is_err());
278 assert!(matches!(
279 get_result.unwrap_err(),
280 RepositoryError::NotFound(_)
281 ));
282
283 let result = impl_repo.delete_by_id("non-existent".to_string()).await;
285 assert!(result.is_err());
286 assert!(matches!(result.unwrap_err(), RepositoryError::NotFound(_)));
287 }
288
289 #[actix_web::test]
290 async fn test_has_entries() {
291 let repo = InMemorySignerRepository::new();
292 assert!(!repo.has_entries().await.unwrap());
293
294 let signer = create_local_signer("test".to_string());
295 repo.create(signer.clone()).await.unwrap();
296 assert!(repo.has_entries().await.unwrap());
297 }
298
299 #[actix_web::test]
300 async fn test_drop_all_entries() {
301 let repo = InMemorySignerRepository::new();
302 let signer = create_local_signer("test".to_string());
303 repo.create(signer.clone()).await.unwrap();
304 assert!(repo.has_entries().await.unwrap());
305
306 repo.drop_all_entries().await.unwrap();
307 assert!(!repo.has_entries().await.unwrap());
308 }
309}
310
311#[cfg(test)]
312mockall::mock! {
313 pub SignerRepository {}
314
315 #[async_trait]
316 impl Repository<SignerRepoModel, String> for SignerRepository {
317 async fn create(&self, entity: SignerRepoModel) -> Result<SignerRepoModel, RepositoryError>;
318 async fn get_by_id(&self, id: String) -> Result<SignerRepoModel, RepositoryError>;
319 async fn list_all(&self) -> Result<Vec<SignerRepoModel>, RepositoryError>;
320 async fn list_paginated(&self, query: PaginationQuery) -> Result<PaginatedResult<SignerRepoModel>, RepositoryError>;
321 async fn update(&self, id: String, entity: SignerRepoModel) -> Result<SignerRepoModel, RepositoryError>;
322 async fn delete_by_id(&self, id: String) -> Result<(), RepositoryError>;
323 async fn count(&self) -> Result<usize, RepositoryError>;
324 async fn has_entries(&self) -> Result<bool, RepositoryError>;
325 async fn drop_all_entries(&self) -> Result<(), RepositoryError>;
326 }
327}