openzeppelin_relayer/bootstrap/
initialize_relayers.rs1use crate::{
6 domain::{get_network_relayer, Relayer},
7 jobs::JobProducerTrait,
8 models::{
9 NetworkRepoModel, NotificationRepoModel, RelayerRepoModel, SignerRepoModel,
10 ThinDataAppState, TransactionRepoModel,
11 },
12 repositories::{
13 ApiKeyRepositoryTrait, NetworkRepository, PluginRepositoryTrait, RelayerRepository,
14 Repository, TransactionCounterTrait, TransactionRepository,
15 },
16};
17use color_eyre::{eyre::WrapErr, Result};
18use futures::future::try_join_all;
19use tracing::debug;
20
21async fn initialize_relayer_with_service<R>(relayer_id: &str, relayer_service: &R) -> Result<()>
25where
26 R: Relayer,
27{
28 debug!(relayer_id = %relayer_id, "initializing relayer");
29
30 relayer_service
31 .initialize_relayer()
32 .await
33 .wrap_err_with(|| format!("Failed to initialize relayer: {relayer_id}"))?;
34
35 Ok(())
36}
37
38pub async fn initialize_relayer<J, RR, TR, NR, NFR, SR, TCR, PR, AKR>(
39 relayer_id: String,
40 app_state: ThinDataAppState<J, RR, TR, NR, NFR, SR, TCR, PR, AKR>,
41) -> Result<()>
42where
43 J: JobProducerTrait + Send + Sync + 'static,
44 RR: RelayerRepository + Repository<RelayerRepoModel, String> + Send + Sync + 'static,
45 TR: TransactionRepository + Repository<TransactionRepoModel, String> + Send + Sync + 'static,
46 NR: NetworkRepository + Repository<NetworkRepoModel, String> + Send + Sync + 'static,
47 NFR: Repository<NotificationRepoModel, String> + Send + Sync + 'static,
48 SR: Repository<SignerRepoModel, String> + Send + Sync + 'static,
49 TCR: TransactionCounterTrait + Send + Sync + 'static,
50 PR: PluginRepositoryTrait + Send + Sync + 'static,
51 AKR: ApiKeyRepositoryTrait + Send + Sync + 'static,
52{
53 let relayer_service = get_network_relayer(relayer_id.clone(), &app_state).await?;
54
55 initialize_relayer_with_service(&relayer_id, &relayer_service).await
56}
57
58pub fn get_relayer_ids_to_initialize(relayers: &[RelayerRepoModel]) -> Vec<String> {
60 relayers.iter().map(|r| r.id.clone()).collect()
61}
62
63pub async fn initialize_relayers<J, RR, TR, NR, NFR, SR, TCR, PR, AKR>(
64 app_state: ThinDataAppState<J, RR, TR, NR, NFR, SR, TCR, PR, AKR>,
65) -> Result<()>
66where
67 J: JobProducerTrait + Send + Sync + 'static,
68 RR: RelayerRepository + Repository<RelayerRepoModel, String> + Send + Sync + 'static,
69 TR: TransactionRepository + Repository<TransactionRepoModel, String> + Send + Sync + 'static,
70 NR: NetworkRepository + Repository<NetworkRepoModel, String> + Send + Sync + 'static,
71 NFR: Repository<NotificationRepoModel, String> + Send + Sync + 'static,
72 SR: Repository<SignerRepoModel, String> + Send + Sync + 'static,
73 TCR: TransactionCounterTrait + Send + Sync + 'static,
74 PR: PluginRepositoryTrait + Send + Sync + 'static,
75 AKR: ApiKeyRepositoryTrait + Send + Sync + 'static,
76{
77 let relayers = app_state.relayer_repository.list_all().await?;
78
79 if relayers.is_empty() {
81 debug!("No relayers to initialize");
82 return Ok(());
83 }
84
85 debug!(count = relayers.len(), "Initializing relayers concurrently");
86
87 let relayer_futures = relayers.iter().map(|relayer| {
88 let app_state = app_state.clone();
89 async move { initialize_relayer(relayer.id.clone(), app_state).await }
90 });
91
92 try_join_all(relayer_futures)
93 .await
94 .wrap_err("Failed to initialize relayers")?;
95
96 debug!(
97 count = relayers.len(),
98 "All relayers initialized successfully"
99 );
100 Ok(())
101}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106 use crate::utils::mocks::mockutils::create_mock_relayer;
107
108 #[test]
109 fn test_get_relayer_ids_with_empty_list() {
110 let relayers: Vec<RelayerRepoModel> = vec![];
111 let ids = get_relayer_ids_to_initialize(&relayers);
112
113 assert_eq!(ids.len(), 0, "Should return empty list for no relayers");
114 }
115
116 #[test]
117 fn test_get_relayer_ids_with_single_relayer() {
118 let relayers = vec![create_mock_relayer("relayer-1".to_string(), false)];
119
120 let ids = get_relayer_ids_to_initialize(&relayers);
121
122 assert_eq!(ids.len(), 1, "Should return one ID");
123 assert_eq!(ids[0], "relayer-1");
124 }
125
126 #[test]
127 fn test_get_relayer_ids_with_multiple_relayers() {
128 let relayers = vec![
129 create_mock_relayer("evm-relayer".to_string(), false),
130 create_mock_relayer("solana-relayer".to_string(), false),
131 create_mock_relayer("stellar-relayer".to_string(), false),
132 ];
133
134 let ids = get_relayer_ids_to_initialize(&relayers);
135
136 assert_eq!(ids.len(), 3, "Should return three IDs");
137 assert_eq!(ids[0], "evm-relayer");
138 assert_eq!(ids[1], "solana-relayer");
139 assert_eq!(ids[2], "stellar-relayer");
140 }
141
142 #[test]
143 fn test_get_relayer_ids_with_mixed_states() {
144 let mut relayers = vec![
145 create_mock_relayer("active-relayer".to_string(), false),
146 create_mock_relayer("paused-relayer".to_string(), false),
147 create_mock_relayer("disabled-relayer".to_string(), false),
148 ];
149
150 relayers[1].paused = true;
152 relayers[2].system_disabled = true;
153
154 let ids = get_relayer_ids_to_initialize(&relayers);
155
156 assert_eq!(
158 ids.len(),
159 3,
160 "Should include all relayers regardless of state"
161 );
162 assert!(ids.contains(&"active-relayer".to_string()));
163 assert!(ids.contains(&"paused-relayer".to_string()));
164 assert!(ids.contains(&"disabled-relayer".to_string()));
165 }
166
167 #[test]
168 fn test_get_relayer_ids_with_different_network_types() {
169 let relayers = vec![
170 create_mock_relayer("evm-1".to_string(), false),
171 create_mock_relayer("evm-2".to_string(), false),
172 create_mock_relayer("solana-1".to_string(), false),
173 create_mock_relayer("stellar-1".to_string(), false),
174 ];
175
176 let ids = get_relayer_ids_to_initialize(&relayers);
177
178 assert_eq!(ids.len(), 4, "Should include all network types");
179
180 assert!(ids.iter().any(|id| id.starts_with("evm-")));
182 assert!(ids.iter().any(|id| id.starts_with("solana-")));
183 assert!(ids.iter().any(|id| id.starts_with("stellar-")));
184 }
185
186 #[test]
187 fn test_concurrent_initialization_count() {
188 let test_cases = vec![
192 (0, 0), (1, 1), (5, 5), (10, 10), ];
197
198 for (relayer_count, expected_init_count) in test_cases {
199 let relayers: Vec<RelayerRepoModel> = (0..relayer_count)
200 .map(|i| create_mock_relayer(format!("relayer-{i}"), false))
201 .collect();
202
203 let ids = get_relayer_ids_to_initialize(&relayers);
204
205 assert_eq!(
206 ids.len(),
207 expected_init_count,
208 "Should create {expected_init_count} initializations for {relayer_count} relayers"
209 );
210 }
211 }
212
213 #[tokio::test]
214 async fn test_initialize_relayer_with_service_success() {
215 use crate::domain::MockRelayer;
216
217 let mut mock_relayer = MockRelayer::new();
218 mock_relayer
219 .expect_initialize_relayer()
220 .times(1)
221 .returning(|| Box::pin(async { Ok(()) }));
222
223 let result = initialize_relayer_with_service("test-relayer", &mock_relayer).await;
224
225 assert!(result.is_ok(), "Should successfully initialize relayer");
226 }
227
228 #[tokio::test]
229 async fn test_initialize_relayer_with_service_failure() {
230 use crate::domain::MockRelayer;
231 use crate::models::RelayerError;
232
233 let mut mock_relayer = MockRelayer::new();
234 mock_relayer
235 .expect_initialize_relayer()
236 .times(1)
237 .returning(|| {
238 Box::pin(async {
239 Err(RelayerError::ProviderError(
240 "RPC connection failed".to_string(),
241 ))
242 })
243 });
244
245 let result = initialize_relayer_with_service("test-relayer", &mock_relayer).await;
246
247 assert!(
248 result.is_err(),
249 "Should fail when initialize_relayer returns error"
250 );
251 let err = result.unwrap_err();
252 assert!(err
253 .to_string()
254 .contains("Failed to initialize relayer: test-relayer"));
255 }
256
257 #[tokio::test]
258 async fn test_initialize_relayer_with_service_called_once() {
259 use crate::domain::MockRelayer;
260
261 let mut mock_relayer = MockRelayer::new();
262 mock_relayer
264 .expect_initialize_relayer()
265 .times(1)
266 .returning(|| Box::pin(async { Ok(()) }));
267
268 let _ = initialize_relayer_with_service("relayer-123", &mock_relayer).await;
269
270 }
272
273 #[tokio::test]
274 async fn test_initialize_relayer_with_service_multiple_relayers() {
275 use crate::domain::MockRelayer;
276
277 let mut mock_relayer_1 = MockRelayer::new();
279 mock_relayer_1
280 .expect_initialize_relayer()
281 .times(1)
282 .returning(|| Box::pin(async { Ok(()) }));
283
284 let mut mock_relayer_2 = MockRelayer::new();
285 mock_relayer_2
286 .expect_initialize_relayer()
287 .times(1)
288 .returning(|| Box::pin(async { Ok(()) }));
289
290 let result1 = initialize_relayer_with_service("relayer-1", &mock_relayer_1).await;
291 let result2 = initialize_relayer_with_service("relayer-2", &mock_relayer_2).await;
292
293 assert!(
294 result1.is_ok(),
295 "First relayer should initialize successfully"
296 );
297 assert!(
298 result2.is_ok(),
299 "Second relayer should initialize successfully"
300 );
301 }
302}