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 {} initializations for {} relayers",
209 expected_init_count,
210 relayer_count
211 );
212 }
213 }
214
215 #[tokio::test]
216 async fn test_initialize_relayer_with_service_success() {
217 use crate::domain::MockRelayer;
218
219 let mut mock_relayer = MockRelayer::new();
220 mock_relayer
221 .expect_initialize_relayer()
222 .times(1)
223 .returning(|| Box::pin(async { Ok(()) }));
224
225 let result = initialize_relayer_with_service("test-relayer", &mock_relayer).await;
226
227 assert!(result.is_ok(), "Should successfully initialize relayer");
228 }
229
230 #[tokio::test]
231 async fn test_initialize_relayer_with_service_failure() {
232 use crate::domain::MockRelayer;
233 use crate::models::RelayerError;
234
235 let mut mock_relayer = MockRelayer::new();
236 mock_relayer
237 .expect_initialize_relayer()
238 .times(1)
239 .returning(|| {
240 Box::pin(async {
241 Err(RelayerError::ProviderError(
242 "RPC connection failed".to_string(),
243 ))
244 })
245 });
246
247 let result = initialize_relayer_with_service("test-relayer", &mock_relayer).await;
248
249 assert!(
250 result.is_err(),
251 "Should fail when initialize_relayer returns error"
252 );
253 let err = result.unwrap_err();
254 assert!(err
255 .to_string()
256 .contains("Failed to initialize relayer: test-relayer"));
257 }
258
259 #[tokio::test]
260 async fn test_initialize_relayer_with_service_called_once() {
261 use crate::domain::MockRelayer;
262
263 let mut mock_relayer = MockRelayer::new();
264 mock_relayer
266 .expect_initialize_relayer()
267 .times(1)
268 .returning(|| Box::pin(async { Ok(()) }));
269
270 let _ = initialize_relayer_with_service("relayer-123", &mock_relayer).await;
271
272 }
274
275 #[tokio::test]
276 async fn test_initialize_relayer_with_service_multiple_relayers() {
277 use crate::domain::MockRelayer;
278
279 let mut mock_relayer_1 = MockRelayer::new();
281 mock_relayer_1
282 .expect_initialize_relayer()
283 .times(1)
284 .returning(|| Box::pin(async { Ok(()) }));
285
286 let mut mock_relayer_2 = MockRelayer::new();
287 mock_relayer_2
288 .expect_initialize_relayer()
289 .times(1)
290 .returning(|| Box::pin(async { Ok(()) }));
291
292 let result1 = initialize_relayer_with_service("relayer-1", &mock_relayer_1).await;
293 let result2 = initialize_relayer_with_service("relayer-2", &mock_relayer_2).await;
294
295 assert!(
296 result1.is_ok(),
297 "First relayer should initialize successfully"
298 );
299 assert!(
300 result2.is_ok(),
301 "Second relayer should initialize successfully"
302 );
303 }
304}