1use crate::models::PaginationQuery;
11use crate::{
12 models::UpdateRelayerRequest,
13 models::{DisabledReason, RelayerNetworkPolicy, RelayerRepoModel, RepositoryError},
14};
15use async_trait::async_trait;
16use eyre::Result;
17use std::collections::HashMap;
18use tokio::sync::{Mutex, MutexGuard};
19
20use crate::repositories::{PaginatedResult, RelayerRepository, Repository};
21
22#[derive(Debug)]
23pub struct InMemoryRelayerRepository {
24 store: Mutex<HashMap<String, RelayerRepoModel>>,
25}
26
27impl InMemoryRelayerRepository {
28 pub fn new() -> Self {
29 Self {
30 store: Mutex::new(HashMap::new()),
31 }
32 }
33 async fn acquire_lock<T>(lock: &Mutex<T>) -> Result<MutexGuard<'_, T>, RepositoryError> {
34 Ok(lock.lock().await)
35 }
36}
37
38impl Default for InMemoryRelayerRepository {
39 fn default() -> Self {
40 Self::new()
41 }
42}
43
44impl Clone for InMemoryRelayerRepository {
45 fn clone(&self) -> Self {
46 let data = self
48 .store
49 .try_lock()
50 .map(|guard| guard.clone())
51 .unwrap_or_else(|_| HashMap::new());
52
53 Self {
54 store: Mutex::new(data),
55 }
56 }
57}
58
59#[async_trait]
60impl RelayerRepository for InMemoryRelayerRepository {
61 async fn list_active(&self) -> Result<Vec<RelayerRepoModel>, RepositoryError> {
62 let store = Self::acquire_lock(&self.store).await?;
63 let active_relayers: Vec<RelayerRepoModel> = store
64 .values()
65 .filter(|&relayer| !relayer.paused)
66 .cloned()
67 .collect();
68 Ok(active_relayers)
69 }
70
71 async fn list_by_signer_id(
72 &self,
73 signer_id: &str,
74 ) -> Result<Vec<RelayerRepoModel>, RepositoryError> {
75 let store = Self::acquire_lock(&self.store).await?;
76 let relayers_with_signer: Vec<RelayerRepoModel> = store
77 .values()
78 .filter(|&relayer| relayer.signer_id == signer_id)
79 .cloned()
80 .collect();
81 Ok(relayers_with_signer)
82 }
83
84 async fn list_by_notification_id(
85 &self,
86 notification_id: &str,
87 ) -> Result<Vec<RelayerRepoModel>, RepositoryError> {
88 let store = Self::acquire_lock(&self.store).await?;
89 let relayers_with_notification: Vec<RelayerRepoModel> = store
90 .values()
91 .filter(|&relayer| {
92 relayer
93 .notification_id
94 .as_ref()
95 .is_some_and(|id| id == notification_id)
96 })
97 .cloned()
98 .collect();
99 Ok(relayers_with_notification)
100 }
101
102 async fn partial_update(
103 &self,
104 id: String,
105 update: UpdateRelayerRequest,
106 ) -> Result<RelayerRepoModel, RepositoryError> {
107 let mut store = Self::acquire_lock(&self.store).await?;
108 if let Some(relayer) = store.get_mut(&id) {
109 if let Some(paused) = update.paused {
110 relayer.paused = paused;
111 }
112 Ok(relayer.clone())
113 } else {
114 Err(RepositoryError::NotFound(format!(
115 "Relayer with ID {id} not found"
116 )))
117 }
118 }
119
120 async fn update_policy(
121 &self,
122 id: String,
123 policy: RelayerNetworkPolicy,
124 ) -> Result<RelayerRepoModel, RepositoryError> {
125 let mut store = Self::acquire_lock(&self.store).await?;
126 let relayer = store
127 .get_mut(&id)
128 .ok_or_else(|| RepositoryError::NotFound(format!("Relayer with ID {id} not found")))?;
129 relayer.policies = policy;
130 Ok(relayer.clone())
131 }
132
133 async fn disable_relayer(
134 &self,
135 relayer_id: String,
136 reason: DisabledReason,
137 ) -> Result<RelayerRepoModel, RepositoryError> {
138 let mut store = self.store.lock().await;
139 if let Some(relayer) = store.get_mut(&relayer_id) {
140 relayer.system_disabled = true;
141 relayer.disabled_reason = Some(reason);
142 Ok(relayer.clone())
143 } else {
144 Err(RepositoryError::NotFound(format!(
145 "Relayer with ID {relayer_id} not found"
146 )))
147 }
148 }
149
150 async fn enable_relayer(
151 &self,
152 relayer_id: String,
153 ) -> Result<RelayerRepoModel, RepositoryError> {
154 let mut store = self.store.lock().await;
155 if let Some(relayer) = store.get_mut(&relayer_id) {
156 relayer.system_disabled = false;
157 relayer.disabled_reason = None;
158 Ok(relayer.clone())
159 } else {
160 Err(RepositoryError::NotFound(format!(
161 "Relayer with ID {relayer_id} not found"
162 )))
163 }
164 }
165
166 fn is_persistent_storage(&self) -> bool {
167 false
168 }
169
170 }
172
173#[async_trait]
174impl Repository<RelayerRepoModel, String> for InMemoryRelayerRepository {
175 async fn create(&self, relayer: RelayerRepoModel) -> Result<RelayerRepoModel, RepositoryError> {
176 let mut store = Self::acquire_lock(&self.store).await?;
177 if store.contains_key(&relayer.id) {
178 return Err(RepositoryError::ConstraintViolation(format!(
179 "Relayer with ID {} already exists",
180 relayer.id
181 )));
182 }
183 store.insert(relayer.id.clone(), relayer.clone());
184 Ok(relayer)
185 }
186
187 async fn get_by_id(&self, id: String) -> Result<RelayerRepoModel, RepositoryError> {
188 let store = Self::acquire_lock(&self.store).await?;
189 match store.get(&id) {
190 Some(relayer) => Ok(relayer.clone()),
191 None => Err(RepositoryError::NotFound(format!(
192 "Relayer with ID {id} not found"
193 ))),
194 }
195 }
196 #[allow(clippy::map_entry)]
197 async fn update(
198 &self,
199 id: String,
200 relayer: RelayerRepoModel,
201 ) -> Result<RelayerRepoModel, RepositoryError> {
202 let mut store = Self::acquire_lock(&self.store).await?;
203 if store.contains_key(&id) {
204 let mut updated_relayer = relayer;
206 updated_relayer.id = id.clone(); store.insert(id, updated_relayer.clone());
208 Ok(updated_relayer)
209 } else {
210 Err(RepositoryError::NotFound(format!(
211 "Relayer with ID {id} not found"
212 )))
213 }
214 }
215
216 async fn delete_by_id(&self, id: String) -> Result<(), RepositoryError> {
217 let mut store = Self::acquire_lock(&self.store).await?;
218 if store.remove(&id).is_some() {
219 Ok(())
220 } else {
221 Err(RepositoryError::NotFound(format!(
222 "Relayer with ID {id} not found"
223 )))
224 }
225 }
226
227 async fn list_all(&self) -> Result<Vec<RelayerRepoModel>, RepositoryError> {
228 let store = Self::acquire_lock(&self.store).await?;
229 Ok(store.values().cloned().collect())
230 }
231
232 async fn list_paginated(
233 &self,
234 query: PaginationQuery,
235 ) -> Result<PaginatedResult<RelayerRepoModel>, RepositoryError> {
236 let total = self.count().await?;
237 let start = ((query.page - 1) * query.per_page) as usize;
238 let items = self
239 .store
240 .lock()
241 .await
242 .values()
243 .skip(start)
244 .take(query.per_page as usize)
245 .cloned()
246 .collect();
247 Ok(PaginatedResult {
248 items,
249 total: total as u64,
250 page: query.page,
251 per_page: query.per_page,
252 })
253 }
254
255 async fn count(&self) -> Result<usize, RepositoryError> {
256 Ok(self.store.lock().await.len())
257 }
258
259 async fn has_entries(&self) -> Result<bool, RepositoryError> {
260 let store = Self::acquire_lock(&self.store).await?;
261 Ok(!store.is_empty())
262 }
263
264 async fn drop_all_entries(&self) -> Result<(), RepositoryError> {
265 let mut store = Self::acquire_lock(&self.store).await?;
266 store.clear();
267 Ok(())
268 }
269}
270
271#[cfg(test)]
272mod tests {
273 use crate::models::{NetworkType, RelayerEvmPolicy};
274
275 use super::*;
276
277 fn create_test_relayer(id: String) -> RelayerRepoModel {
278 RelayerRepoModel {
279 id: id.clone(),
280 name: format!("Relayer {}", id.clone()),
281 network: "TestNet".to_string(),
282 paused: false,
283 network_type: NetworkType::Evm,
284 policies: RelayerNetworkPolicy::Evm(RelayerEvmPolicy {
285 include_revert_data: None,
286 gas_price_cap: None,
287 whitelist_receivers: None,
288 eip1559_pricing: Some(false),
289 private_transactions: Some(false),
290 min_balance: Some(0),
291 gas_limit_estimation: Some(true),
292 }),
293 signer_id: "test".to_string(),
294 address: "0x".to_string(),
295 notification_id: None,
296 system_disabled: false,
297 custom_rpc_urls: None,
298 ..Default::default()
299 }
300 }
301
302 #[actix_web::test]
303 async fn test_new_repository_is_empty() {
304 let repo = InMemoryRelayerRepository::new();
305 assert_eq!(repo.count().await.unwrap(), 0);
306 }
307
308 #[actix_web::test]
309 async fn test_add_relayer() {
310 let repo = InMemoryRelayerRepository::new();
311 let relayer = create_test_relayer("test".to_string());
312
313 repo.create(relayer.clone()).await.unwrap();
314 assert_eq!(repo.count().await.unwrap(), 1);
315
316 let stored = repo.get_by_id("test".to_string()).await.unwrap();
317 assert_eq!(stored.id, relayer.id);
318 assert_eq!(stored.name, relayer.name);
319 }
320
321 #[actix_web::test]
322 async fn test_update_relayer() {
323 let repo = InMemoryRelayerRepository::new();
324 let mut relayer = create_test_relayer("test".to_string());
325
326 repo.create(relayer.clone()).await.unwrap();
327
328 relayer.name = "Updated Name".to_string();
329 repo.update("test".to_string(), relayer.clone())
330 .await
331 .unwrap();
332
333 let updated = repo.get_by_id("test".to_string()).await.unwrap();
334 assert_eq!(updated.name, "Updated Name");
335 }
336
337 #[actix_web::test]
338 async fn test_list_relayers() {
339 let repo = InMemoryRelayerRepository::new();
340 let relayer1 = create_test_relayer("test".to_string());
341 let relayer2 = create_test_relayer("test2".to_string());
342
343 repo.create(relayer1.clone()).await.unwrap();
344 repo.create(relayer2).await.unwrap();
345
346 let relayers = repo.list_all().await.unwrap();
347 assert_eq!(relayers.len(), 2);
348 }
349
350 #[actix_web::test]
351 async fn test_list_active_relayers() {
352 let repo = InMemoryRelayerRepository::new();
353 let relayer1 = create_test_relayer("test".to_string());
354 let mut relayer2 = create_test_relayer("test2".to_string());
355
356 relayer2.paused = true;
357
358 repo.create(relayer1.clone()).await.unwrap();
359 repo.create(relayer2).await.unwrap();
360
361 let active_relayers = repo.list_active().await.unwrap();
362 assert_eq!(active_relayers.len(), 1);
363 assert_eq!(active_relayers[0].id, "test".to_string());
364 }
365
366 #[actix_web::test]
367 async fn test_update_nonexistent_relayer() {
368 let repo = InMemoryRelayerRepository::new();
369 let relayer = create_test_relayer("test".to_string());
370
371 let result = repo.update("test".to_string(), relayer).await;
372 assert!(matches!(result, Err(RepositoryError::NotFound(_))));
373 }
374
375 #[actix_web::test]
376 async fn test_get_nonexistent_relayer() {
377 let repo = InMemoryRelayerRepository::new();
378
379 let result = repo.get_by_id("test".to_string()).await;
380 assert!(matches!(result, Err(RepositoryError::NotFound(_))));
381 }
382
383 #[actix_web::test]
384 async fn test_partial_update_relayer() {
385 let repo = InMemoryRelayerRepository::new();
386
387 let relayer_id = "test_relayer".to_string();
389 let initial_relayer = create_test_relayer(relayer_id.clone());
390
391 repo.create(initial_relayer.clone()).await.unwrap();
392
393 let update_req = UpdateRelayerRequest {
395 name: None,
396 paused: Some(true),
397 policies: None,
398 notification_id: None,
399 custom_rpc_urls: None,
400 };
401
402 let updated_relayer = repo
403 .partial_update(relayer_id.clone(), update_req)
404 .await
405 .unwrap();
406
407 assert_eq!(updated_relayer.id, initial_relayer.id);
408 assert!(updated_relayer.paused);
409 }
410
411 #[actix_web::test]
412 async fn test_disable_relayer() {
413 let repo = InMemoryRelayerRepository::new();
414
415 let relayer_id = "test_relayer".to_string();
417 let initial_relayer = create_test_relayer(relayer_id.clone());
418
419 repo.create(initial_relayer.clone()).await.unwrap();
420
421 let disabled_relayer = repo
423 .disable_relayer(
424 relayer_id.clone(),
425 DisabledReason::BalanceCheckFailed("test reason".to_string()),
426 )
427 .await
428 .unwrap();
429
430 assert_eq!(disabled_relayer.id, initial_relayer.id);
431 assert!(disabled_relayer.system_disabled);
432 assert_eq!(
433 disabled_relayer.disabled_reason,
434 Some(DisabledReason::BalanceCheckFailed(
435 "test reason".to_string()
436 ))
437 );
438 }
439
440 #[actix_web::test]
441 async fn test_enable_relayer() {
442 let repo = InMemoryRelayerRepository::new();
443
444 let relayer_id = "test_relayer".to_string();
446 let mut initial_relayer = create_test_relayer(relayer_id.clone());
447
448 initial_relayer.system_disabled = true;
449
450 repo.create(initial_relayer.clone()).await.unwrap();
451
452 let enabled_relayer = repo.enable_relayer(relayer_id.clone()).await.unwrap();
454
455 assert_eq!(enabled_relayer.id, initial_relayer.id);
456 assert!(!enabled_relayer.system_disabled);
457 }
458
459 #[actix_web::test]
460 async fn test_update_policy() {
461 let repo = InMemoryRelayerRepository::new();
462 let relayer = create_test_relayer("test".to_string());
463
464 repo.create(relayer.clone()).await.unwrap();
465
466 let new_policy = RelayerNetworkPolicy::Evm(RelayerEvmPolicy {
468 include_revert_data: None,
469 gas_price_cap: Some(50000000000),
470 whitelist_receivers: Some(vec!["0x1234".to_string()]),
471 eip1559_pricing: Some(true),
472 private_transactions: Some(true),
473 min_balance: Some(1000000),
474 gas_limit_estimation: Some(true),
475 });
476
477 let updated_relayer = repo
479 .update_policy("test".to_string(), new_policy.clone())
480 .await
481 .unwrap();
482
483 match updated_relayer.policies {
485 RelayerNetworkPolicy::Evm(policy) => {
486 assert_eq!(policy.gas_price_cap, Some(50000000000));
487 assert_eq!(policy.whitelist_receivers, Some(vec!["0x1234".to_string()]));
488 assert_eq!(policy.eip1559_pricing, Some(true));
489 assert!(policy.private_transactions.unwrap_or(false));
490 assert_eq!(policy.min_balance, Some(1000000));
491 }
492 _ => panic!("Unexpected policy type"),
493 }
494 }
495
496 #[actix_web::test]
498 async fn test_has_entries() {
499 let repo = InMemoryRelayerRepository::new();
500 assert!(!repo.has_entries().await.unwrap());
501
502 let relayer = create_test_relayer("test".to_string());
503
504 repo.create(relayer.clone()).await.unwrap();
505 assert!(repo.has_entries().await.unwrap());
506 }
507
508 #[actix_web::test]
509 async fn test_drop_all_entries() {
510 let repo = InMemoryRelayerRepository::new();
511 let relayer = create_test_relayer("test".to_string());
512
513 repo.create(relayer.clone()).await.unwrap();
514
515 assert!(repo.has_entries().await.unwrap());
516
517 repo.drop_all_entries().await.unwrap();
518 assert!(!repo.has_entries().await.unwrap());
519 }
520
521 #[actix_web::test]
522 async fn test_list_by_signer_id() {
523 let repo = InMemoryRelayerRepository::new();
524
525 let relayer1 = RelayerRepoModel {
527 id: "relayer-1".to_string(),
528 name: "Relayer 1".to_string(),
529 network: "ethereum".to_string(),
530 paused: false,
531 network_type: NetworkType::Evm,
532 signer_id: "signer-alpha".to_string(),
533 policies: RelayerNetworkPolicy::Evm(RelayerEvmPolicy::default()),
534 address: "0x1111".to_string(),
535 notification_id: None,
536 system_disabled: false,
537 custom_rpc_urls: None,
538 ..Default::default()
539 };
540
541 let relayer2 = RelayerRepoModel {
542 id: "relayer-2".to_string(),
543 name: "Relayer 2".to_string(),
544 network: "polygon".to_string(),
545 paused: true,
546 network_type: NetworkType::Evm,
547 signer_id: "signer-alpha".to_string(), policies: RelayerNetworkPolicy::Evm(RelayerEvmPolicy::default()),
549 address: "0x2222".to_string(),
550 notification_id: None,
551 system_disabled: false,
552 custom_rpc_urls: None,
553 ..Default::default()
554 };
555
556 let relayer3 = RelayerRepoModel {
557 id: "relayer-3".to_string(),
558 name: "Relayer 3".to_string(),
559 network: "solana".to_string(),
560 paused: false,
561 network_type: NetworkType::Solana,
562 signer_id: "signer-beta".to_string(), policies: RelayerNetworkPolicy::Solana(crate::models::RelayerSolanaPolicy::default()),
564 address: "solana-addr".to_string(),
565 notification_id: None,
566 system_disabled: false,
567 custom_rpc_urls: None,
568 ..Default::default()
569 };
570
571 let relayer4 = RelayerRepoModel {
572 id: "relayer-4".to_string(),
573 name: "Relayer 4".to_string(),
574 network: "stellar".to_string(),
575 paused: false,
576 network_type: NetworkType::Stellar,
577 signer_id: "signer-alpha".to_string(), policies: RelayerNetworkPolicy::Stellar(crate::models::RelayerStellarPolicy::default()),
579 address: "stellar-addr".to_string(),
580 notification_id: Some("notification-1".to_string()),
581 system_disabled: true,
582 custom_rpc_urls: None,
583 ..Default::default()
584 };
585
586 repo.create(relayer1).await.unwrap();
588 repo.create(relayer2).await.unwrap();
589 repo.create(relayer3).await.unwrap();
590 repo.create(relayer4).await.unwrap();
591
592 let relayers_with_alpha = repo.list_by_signer_id("signer-alpha").await.unwrap();
594 assert_eq!(relayers_with_alpha.len(), 3);
595
596 let alpha_ids: Vec<String> = relayers_with_alpha.iter().map(|r| r.id.clone()).collect();
597 assert!(alpha_ids.contains(&"relayer-1".to_string()));
598 assert!(alpha_ids.contains(&"relayer-2".to_string()));
599 assert!(alpha_ids.contains(&"relayer-4".to_string()));
600 assert!(!alpha_ids.contains(&"relayer-3".to_string()));
601
602 let relayer2_found = relayers_with_alpha
604 .iter()
605 .find(|r| r.id == "relayer-2")
606 .unwrap();
607 let relayer4_found = relayers_with_alpha
608 .iter()
609 .find(|r| r.id == "relayer-4")
610 .unwrap();
611 assert!(relayer2_found.paused); assert!(relayer4_found.system_disabled); let relayers_with_beta = repo.list_by_signer_id("signer-beta").await.unwrap();
616 assert_eq!(relayers_with_beta.len(), 1);
617 assert_eq!(relayers_with_beta[0].id, "relayer-3");
618 assert_eq!(relayers_with_beta[0].network_type, NetworkType::Solana);
619
620 let relayers_with_gamma = repo.list_by_signer_id("signer-gamma").await.unwrap();
622 assert_eq!(relayers_with_gamma.len(), 0);
623
624 let relayers_with_empty = repo.list_by_signer_id("").await.unwrap();
626 assert_eq!(relayers_with_empty.len(), 0);
627
628 assert_eq!(repo.count().await.unwrap(), 4);
630
631 repo.delete_by_id("relayer-2".to_string()).await.unwrap();
633
634 let relayers_with_alpha_after_delete =
635 repo.list_by_signer_id("signer-alpha").await.unwrap();
636 assert_eq!(relayers_with_alpha_after_delete.len(), 2); let alpha_ids_after: Vec<String> = relayers_with_alpha_after_delete
639 .iter()
640 .map(|r| r.id.clone())
641 .collect();
642 assert!(alpha_ids_after.contains(&"relayer-1".to_string()));
643 assert!(!alpha_ids_after.contains(&"relayer-2".to_string())); assert!(alpha_ids_after.contains(&"relayer-4".to_string()));
645 }
646
647 #[actix_web::test]
648 async fn test_list_by_notification_id() {
649 let repo = InMemoryRelayerRepository::new();
650
651 let relayer1 = RelayerRepoModel {
653 id: "relayer-1".to_string(),
654 name: "Relayer 1".to_string(),
655 network: "ethereum".to_string(),
656 paused: false,
657 network_type: NetworkType::Evm,
658 signer_id: "test-signer".to_string(),
659 policies: RelayerNetworkPolicy::Evm(RelayerEvmPolicy::default()),
660 address: "0x1111".to_string(),
661 notification_id: Some("notification-alpha".to_string()),
662 system_disabled: false,
663 custom_rpc_urls: None,
664 ..Default::default()
665 };
666
667 let relayer2 = RelayerRepoModel {
668 id: "relayer-2".to_string(),
669 name: "Relayer 2".to_string(),
670 network: "polygon".to_string(),
671 paused: true,
672 network_type: NetworkType::Evm,
673 signer_id: "test-signer".to_string(),
674 policies: RelayerNetworkPolicy::Evm(RelayerEvmPolicy::default()),
675 address: "0x2222".to_string(),
676 notification_id: Some("notification-alpha".to_string()), system_disabled: false,
678 custom_rpc_urls: None,
679 ..Default::default()
680 };
681
682 let relayer3 = RelayerRepoModel {
683 id: "relayer-3".to_string(),
684 name: "Relayer 3".to_string(),
685 network: "solana".to_string(),
686 paused: false,
687 network_type: NetworkType::Solana,
688 signer_id: "test-signer".to_string(),
689 policies: RelayerNetworkPolicy::Solana(crate::models::RelayerSolanaPolicy::default()),
690 address: "solana-addr".to_string(),
691 notification_id: Some("notification-beta".to_string()), system_disabled: false,
693 custom_rpc_urls: None,
694 ..Default::default()
695 };
696
697 let relayer4 = RelayerRepoModel {
698 id: "relayer-4".to_string(),
699 name: "Relayer 4".to_string(),
700 network: "stellar".to_string(),
701 paused: false,
702 network_type: NetworkType::Stellar,
703 signer_id: "test-signer".to_string(),
704 policies: RelayerNetworkPolicy::Stellar(crate::models::RelayerStellarPolicy::default()),
705 address: "stellar-addr".to_string(),
706 notification_id: None, system_disabled: true,
708 custom_rpc_urls: None,
709 ..Default::default()
710 };
711
712 let relayer5 = RelayerRepoModel {
713 id: "relayer-5".to_string(),
714 name: "Relayer 5".to_string(),
715 network: "bsc".to_string(),
716 paused: false,
717 network_type: NetworkType::Evm,
718 signer_id: "test-signer".to_string(),
719 policies: RelayerNetworkPolicy::Evm(RelayerEvmPolicy::default()),
720 address: "0x5555".to_string(),
721 notification_id: Some("notification-alpha".to_string()), system_disabled: false,
723 custom_rpc_urls: None,
724 ..Default::default()
725 };
726
727 repo.create(relayer1).await.unwrap();
729 repo.create(relayer2).await.unwrap();
730 repo.create(relayer3).await.unwrap();
731 repo.create(relayer4).await.unwrap();
732 repo.create(relayer5).await.unwrap();
733
734 let relayers_with_alpha = repo
736 .list_by_notification_id("notification-alpha")
737 .await
738 .unwrap();
739 assert_eq!(relayers_with_alpha.len(), 3);
740
741 let alpha_ids: Vec<String> = relayers_with_alpha.iter().map(|r| r.id.clone()).collect();
742 assert!(alpha_ids.contains(&"relayer-1".to_string()));
743 assert!(alpha_ids.contains(&"relayer-2".to_string()));
744 assert!(alpha_ids.contains(&"relayer-5".to_string()));
745 assert!(!alpha_ids.contains(&"relayer-3".to_string()));
746 assert!(!alpha_ids.contains(&"relayer-4".to_string()));
747
748 let relayer2_found = relayers_with_alpha
750 .iter()
751 .find(|r| r.id == "relayer-2")
752 .unwrap();
753 let relayer5_found = relayers_with_alpha
754 .iter()
755 .find(|r| r.id == "relayer-5")
756 .unwrap();
757 assert!(relayer2_found.paused); assert_eq!(relayer5_found.network, "bsc"); let relayers_with_beta = repo
762 .list_by_notification_id("notification-beta")
763 .await
764 .unwrap();
765 assert_eq!(relayers_with_beta.len(), 1);
766 assert_eq!(relayers_with_beta[0].id, "relayer-3");
767 assert_eq!(relayers_with_beta[0].network_type, NetworkType::Solana);
768
769 let relayers_with_gamma = repo
771 .list_by_notification_id("notification-gamma")
772 .await
773 .unwrap();
774 assert_eq!(relayers_with_gamma.len(), 0);
775
776 let relayers_with_empty = repo.list_by_notification_id("").await.unwrap();
778 assert_eq!(relayers_with_empty.len(), 0);
779
780 assert_eq!(repo.count().await.unwrap(), 5);
782
783 repo.delete_by_id("relayer-2".to_string()).await.unwrap();
785
786 let relayers_with_alpha_after_delete = repo
787 .list_by_notification_id("notification-alpha")
788 .await
789 .unwrap();
790 assert_eq!(relayers_with_alpha_after_delete.len(), 2); let alpha_ids_after: Vec<String> = relayers_with_alpha_after_delete
793 .iter()
794 .map(|r| r.id.clone())
795 .collect();
796 assert!(alpha_ids_after.contains(&"relayer-1".to_string()));
797 assert!(!alpha_ids_after.contains(&"relayer-2".to_string())); assert!(alpha_ids_after.contains(&"relayer-5".to_string()));
799
800 let mut updated_relayer = repo.get_by_id("relayer-5".to_string()).await.unwrap();
802 updated_relayer.notification_id = Some("notification-beta".to_string());
803 repo.update("relayer-5".to_string(), updated_relayer)
804 .await
805 .unwrap();
806
807 let relayers_with_alpha_final = repo
809 .list_by_notification_id("notification-alpha")
810 .await
811 .unwrap();
812 assert_eq!(relayers_with_alpha_final.len(), 1);
813 assert_eq!(relayers_with_alpha_final[0].id, "relayer-1");
814
815 let relayers_with_beta_final = repo
817 .list_by_notification_id("notification-beta")
818 .await
819 .unwrap();
820 assert_eq!(relayers_with_beta_final.len(), 2);
821 let beta_ids_final: Vec<String> = relayers_with_beta_final
822 .iter()
823 .map(|r| r.id.clone())
824 .collect();
825 assert!(beta_ids_final.contains(&"relayer-3".to_string()));
826 assert!(beta_ids_final.contains(&"relayer-5".to_string()));
827 }
828}