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 {} not found",
116 id
117 )))
118 }
119 }
120
121 async fn update_policy(
122 &self,
123 id: String,
124 policy: RelayerNetworkPolicy,
125 ) -> Result<RelayerRepoModel, RepositoryError> {
126 let mut store = Self::acquire_lock(&self.store).await?;
127 let relayer = store.get_mut(&id).ok_or_else(|| {
128 RepositoryError::NotFound(format!("Relayer with ID {} not found", id))
129 })?;
130 relayer.policies = policy;
131 Ok(relayer.clone())
132 }
133
134 async fn disable_relayer(
135 &self,
136 relayer_id: String,
137 reason: DisabledReason,
138 ) -> Result<RelayerRepoModel, RepositoryError> {
139 let mut store = self.store.lock().await;
140 if let Some(relayer) = store.get_mut(&relayer_id) {
141 relayer.system_disabled = true;
142 relayer.disabled_reason = Some(reason);
143 Ok(relayer.clone())
144 } else {
145 Err(RepositoryError::NotFound(format!(
146 "Relayer with ID {} not found",
147 relayer_id
148 )))
149 }
150 }
151
152 async fn enable_relayer(
153 &self,
154 relayer_id: String,
155 ) -> Result<RelayerRepoModel, RepositoryError> {
156 let mut store = self.store.lock().await;
157 if let Some(relayer) = store.get_mut(&relayer_id) {
158 relayer.system_disabled = false;
159 relayer.disabled_reason = None;
160 Ok(relayer.clone())
161 } else {
162 Err(RepositoryError::NotFound(format!(
163 "Relayer with ID {} not found",
164 relayer_id
165 )))
166 }
167 }
168}
169
170#[async_trait]
171impl Repository<RelayerRepoModel, String> for InMemoryRelayerRepository {
172 async fn create(&self, relayer: RelayerRepoModel) -> Result<RelayerRepoModel, RepositoryError> {
173 let mut store = Self::acquire_lock(&self.store).await?;
174 if store.contains_key(&relayer.id) {
175 return Err(RepositoryError::ConstraintViolation(format!(
176 "Relayer with ID {} already exists",
177 relayer.id
178 )));
179 }
180 store.insert(relayer.id.clone(), relayer.clone());
181 Ok(relayer)
182 }
183
184 async fn get_by_id(&self, id: String) -> Result<RelayerRepoModel, RepositoryError> {
185 let store = Self::acquire_lock(&self.store).await?;
186 match store.get(&id) {
187 Some(relayer) => Ok(relayer.clone()),
188 None => Err(RepositoryError::NotFound(format!(
189 "Relayer with ID {} not found",
190 id
191 ))),
192 }
193 }
194 #[allow(clippy::map_entry)]
195 async fn update(
196 &self,
197 id: String,
198 relayer: RelayerRepoModel,
199 ) -> Result<RelayerRepoModel, RepositoryError> {
200 let mut store = Self::acquire_lock(&self.store).await?;
201 if store.contains_key(&id) {
202 let mut updated_relayer = relayer;
204 updated_relayer.id = id.clone(); store.insert(id, updated_relayer.clone());
206 Ok(updated_relayer)
207 } else {
208 Err(RepositoryError::NotFound(format!(
209 "Relayer with ID {} not found",
210 id
211 )))
212 }
213 }
214
215 async fn delete_by_id(&self, id: String) -> Result<(), RepositoryError> {
216 let mut store = Self::acquire_lock(&self.store).await?;
217 if store.remove(&id).is_some() {
218 Ok(())
219 } else {
220 Err(RepositoryError::NotFound(format!(
221 "Relayer with ID {} not found",
222 id
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 gas_price_cap: None,
286 whitelist_receivers: None,
287 eip1559_pricing: Some(false),
288 private_transactions: Some(false),
289 min_balance: Some(0),
290 gas_limit_estimation: Some(true),
291 }),
292 signer_id: "test".to_string(),
293 address: "0x".to_string(),
294 notification_id: None,
295 system_disabled: false,
296 custom_rpc_urls: None,
297 ..Default::default()
298 }
299 }
300
301 #[actix_web::test]
302 async fn test_new_repository_is_empty() {
303 let repo = InMemoryRelayerRepository::new();
304 assert_eq!(repo.count().await.unwrap(), 0);
305 }
306
307 #[actix_web::test]
308 async fn test_add_relayer() {
309 let repo = InMemoryRelayerRepository::new();
310 let relayer = create_test_relayer("test".to_string());
311
312 repo.create(relayer.clone()).await.unwrap();
313 assert_eq!(repo.count().await.unwrap(), 1);
314
315 let stored = repo.get_by_id("test".to_string()).await.unwrap();
316 assert_eq!(stored.id, relayer.id);
317 assert_eq!(stored.name, relayer.name);
318 }
319
320 #[actix_web::test]
321 async fn test_update_relayer() {
322 let repo = InMemoryRelayerRepository::new();
323 let mut relayer = create_test_relayer("test".to_string());
324
325 repo.create(relayer.clone()).await.unwrap();
326
327 relayer.name = "Updated Name".to_string();
328 repo.update("test".to_string(), relayer.clone())
329 .await
330 .unwrap();
331
332 let updated = repo.get_by_id("test".to_string()).await.unwrap();
333 assert_eq!(updated.name, "Updated Name");
334 }
335
336 #[actix_web::test]
337 async fn test_list_relayers() {
338 let repo = InMemoryRelayerRepository::new();
339 let relayer1 = create_test_relayer("test".to_string());
340 let relayer2 = create_test_relayer("test2".to_string());
341
342 repo.create(relayer1.clone()).await.unwrap();
343 repo.create(relayer2).await.unwrap();
344
345 let relayers = repo.list_all().await.unwrap();
346 assert_eq!(relayers.len(), 2);
347 }
348
349 #[actix_web::test]
350 async fn test_list_active_relayers() {
351 let repo = InMemoryRelayerRepository::new();
352 let relayer1 = create_test_relayer("test".to_string());
353 let mut relayer2 = create_test_relayer("test2".to_string());
354
355 relayer2.paused = true;
356
357 repo.create(relayer1.clone()).await.unwrap();
358 repo.create(relayer2).await.unwrap();
359
360 let active_relayers = repo.list_active().await.unwrap();
361 assert_eq!(active_relayers.len(), 1);
362 assert_eq!(active_relayers[0].id, "test".to_string());
363 }
364
365 #[actix_web::test]
366 async fn test_update_nonexistent_relayer() {
367 let repo = InMemoryRelayerRepository::new();
368 let relayer = create_test_relayer("test".to_string());
369
370 let result = repo.update("test".to_string(), relayer).await;
371 assert!(matches!(result, Err(RepositoryError::NotFound(_))));
372 }
373
374 #[actix_web::test]
375 async fn test_get_nonexistent_relayer() {
376 let repo = InMemoryRelayerRepository::new();
377
378 let result = repo.get_by_id("test".to_string()).await;
379 assert!(matches!(result, Err(RepositoryError::NotFound(_))));
380 }
381
382 #[actix_web::test]
383 async fn test_partial_update_relayer() {
384 let repo = InMemoryRelayerRepository::new();
385
386 let relayer_id = "test_relayer".to_string();
388 let initial_relayer = create_test_relayer(relayer_id.clone());
389
390 repo.create(initial_relayer.clone()).await.unwrap();
391
392 let update_req = UpdateRelayerRequest {
394 name: None,
395 paused: Some(true),
396 policies: None,
397 notification_id: None,
398 custom_rpc_urls: None,
399 };
400
401 let updated_relayer = repo
402 .partial_update(relayer_id.clone(), update_req)
403 .await
404 .unwrap();
405
406 assert_eq!(updated_relayer.id, initial_relayer.id);
407 assert!(updated_relayer.paused);
408 }
409
410 #[actix_web::test]
411 async fn test_disable_relayer() {
412 let repo = InMemoryRelayerRepository::new();
413
414 let relayer_id = "test_relayer".to_string();
416 let initial_relayer = create_test_relayer(relayer_id.clone());
417
418 repo.create(initial_relayer.clone()).await.unwrap();
419
420 let disabled_relayer = repo
422 .disable_relayer(
423 relayer_id.clone(),
424 DisabledReason::BalanceCheckFailed("test reason".to_string()),
425 )
426 .await
427 .unwrap();
428
429 assert_eq!(disabled_relayer.id, initial_relayer.id);
430 assert!(disabled_relayer.system_disabled);
431 assert_eq!(
432 disabled_relayer.disabled_reason,
433 Some(DisabledReason::BalanceCheckFailed(
434 "test reason".to_string()
435 ))
436 );
437 }
438
439 #[actix_web::test]
440 async fn test_enable_relayer() {
441 let repo = InMemoryRelayerRepository::new();
442
443 let relayer_id = "test_relayer".to_string();
445 let mut initial_relayer = create_test_relayer(relayer_id.clone());
446
447 initial_relayer.system_disabled = true;
448
449 repo.create(initial_relayer.clone()).await.unwrap();
450
451 let enabled_relayer = repo.enable_relayer(relayer_id.clone()).await.unwrap();
453
454 assert_eq!(enabled_relayer.id, initial_relayer.id);
455 assert!(!enabled_relayer.system_disabled);
456 }
457
458 #[actix_web::test]
459 async fn test_update_policy() {
460 let repo = InMemoryRelayerRepository::new();
461 let relayer = create_test_relayer("test".to_string());
462
463 repo.create(relayer.clone()).await.unwrap();
464
465 let new_policy = RelayerNetworkPolicy::Evm(RelayerEvmPolicy {
467 gas_price_cap: Some(50000000000),
468 whitelist_receivers: Some(vec!["0x1234".to_string()]),
469 eip1559_pricing: Some(true),
470 private_transactions: Some(true),
471 min_balance: Some(1000000),
472 gas_limit_estimation: Some(true),
473 });
474
475 let updated_relayer = repo
477 .update_policy("test".to_string(), new_policy.clone())
478 .await
479 .unwrap();
480
481 match updated_relayer.policies {
483 RelayerNetworkPolicy::Evm(policy) => {
484 assert_eq!(policy.gas_price_cap, Some(50000000000));
485 assert_eq!(policy.whitelist_receivers, Some(vec!["0x1234".to_string()]));
486 assert_eq!(policy.eip1559_pricing, Some(true));
487 assert!(policy.private_transactions.unwrap_or(false));
488 assert_eq!(policy.min_balance, Some(1000000));
489 }
490 _ => panic!("Unexpected policy type"),
491 }
492 }
493
494 #[actix_web::test]
496 async fn test_has_entries() {
497 let repo = InMemoryRelayerRepository::new();
498 assert!(!repo.has_entries().await.unwrap());
499
500 let relayer = create_test_relayer("test".to_string());
501
502 repo.create(relayer.clone()).await.unwrap();
503 assert!(repo.has_entries().await.unwrap());
504 }
505
506 #[actix_web::test]
507 async fn test_drop_all_entries() {
508 let repo = InMemoryRelayerRepository::new();
509 let relayer = create_test_relayer("test".to_string());
510
511 repo.create(relayer.clone()).await.unwrap();
512
513 assert!(repo.has_entries().await.unwrap());
514
515 repo.drop_all_entries().await.unwrap();
516 assert!(!repo.has_entries().await.unwrap());
517 }
518
519 #[actix_web::test]
520 async fn test_list_by_signer_id() {
521 let repo = InMemoryRelayerRepository::new();
522
523 let relayer1 = RelayerRepoModel {
525 id: "relayer-1".to_string(),
526 name: "Relayer 1".to_string(),
527 network: "ethereum".to_string(),
528 paused: false,
529 network_type: NetworkType::Evm,
530 signer_id: "signer-alpha".to_string(),
531 policies: RelayerNetworkPolicy::Evm(RelayerEvmPolicy::default()),
532 address: "0x1111".to_string(),
533 notification_id: None,
534 system_disabled: false,
535 custom_rpc_urls: None,
536 ..Default::default()
537 };
538
539 let relayer2 = RelayerRepoModel {
540 id: "relayer-2".to_string(),
541 name: "Relayer 2".to_string(),
542 network: "polygon".to_string(),
543 paused: true,
544 network_type: NetworkType::Evm,
545 signer_id: "signer-alpha".to_string(), policies: RelayerNetworkPolicy::Evm(RelayerEvmPolicy::default()),
547 address: "0x2222".to_string(),
548 notification_id: None,
549 system_disabled: false,
550 custom_rpc_urls: None,
551 ..Default::default()
552 };
553
554 let relayer3 = RelayerRepoModel {
555 id: "relayer-3".to_string(),
556 name: "Relayer 3".to_string(),
557 network: "solana".to_string(),
558 paused: false,
559 network_type: NetworkType::Solana,
560 signer_id: "signer-beta".to_string(), policies: RelayerNetworkPolicy::Solana(crate::models::RelayerSolanaPolicy::default()),
562 address: "solana-addr".to_string(),
563 notification_id: None,
564 system_disabled: false,
565 custom_rpc_urls: None,
566 ..Default::default()
567 };
568
569 let relayer4 = RelayerRepoModel {
570 id: "relayer-4".to_string(),
571 name: "Relayer 4".to_string(),
572 network: "stellar".to_string(),
573 paused: false,
574 network_type: NetworkType::Stellar,
575 signer_id: "signer-alpha".to_string(), policies: RelayerNetworkPolicy::Stellar(crate::models::RelayerStellarPolicy::default()),
577 address: "stellar-addr".to_string(),
578 notification_id: Some("notification-1".to_string()),
579 system_disabled: true,
580 custom_rpc_urls: None,
581 ..Default::default()
582 };
583
584 repo.create(relayer1).await.unwrap();
586 repo.create(relayer2).await.unwrap();
587 repo.create(relayer3).await.unwrap();
588 repo.create(relayer4).await.unwrap();
589
590 let relayers_with_alpha = repo.list_by_signer_id("signer-alpha").await.unwrap();
592 assert_eq!(relayers_with_alpha.len(), 3);
593
594 let alpha_ids: Vec<String> = relayers_with_alpha.iter().map(|r| r.id.clone()).collect();
595 assert!(alpha_ids.contains(&"relayer-1".to_string()));
596 assert!(alpha_ids.contains(&"relayer-2".to_string()));
597 assert!(alpha_ids.contains(&"relayer-4".to_string()));
598 assert!(!alpha_ids.contains(&"relayer-3".to_string()));
599
600 let relayer2_found = relayers_with_alpha
602 .iter()
603 .find(|r| r.id == "relayer-2")
604 .unwrap();
605 let relayer4_found = relayers_with_alpha
606 .iter()
607 .find(|r| r.id == "relayer-4")
608 .unwrap();
609 assert!(relayer2_found.paused); assert!(relayer4_found.system_disabled); let relayers_with_beta = repo.list_by_signer_id("signer-beta").await.unwrap();
614 assert_eq!(relayers_with_beta.len(), 1);
615 assert_eq!(relayers_with_beta[0].id, "relayer-3");
616 assert_eq!(relayers_with_beta[0].network_type, NetworkType::Solana);
617
618 let relayers_with_gamma = repo.list_by_signer_id("signer-gamma").await.unwrap();
620 assert_eq!(relayers_with_gamma.len(), 0);
621
622 let relayers_with_empty = repo.list_by_signer_id("").await.unwrap();
624 assert_eq!(relayers_with_empty.len(), 0);
625
626 assert_eq!(repo.count().await.unwrap(), 4);
628
629 repo.delete_by_id("relayer-2".to_string()).await.unwrap();
631
632 let relayers_with_alpha_after_delete =
633 repo.list_by_signer_id("signer-alpha").await.unwrap();
634 assert_eq!(relayers_with_alpha_after_delete.len(), 2); let alpha_ids_after: Vec<String> = relayers_with_alpha_after_delete
637 .iter()
638 .map(|r| r.id.clone())
639 .collect();
640 assert!(alpha_ids_after.contains(&"relayer-1".to_string()));
641 assert!(!alpha_ids_after.contains(&"relayer-2".to_string())); assert!(alpha_ids_after.contains(&"relayer-4".to_string()));
643 }
644
645 #[actix_web::test]
646 async fn test_list_by_notification_id() {
647 let repo = InMemoryRelayerRepository::new();
648
649 let relayer1 = RelayerRepoModel {
651 id: "relayer-1".to_string(),
652 name: "Relayer 1".to_string(),
653 network: "ethereum".to_string(),
654 paused: false,
655 network_type: NetworkType::Evm,
656 signer_id: "test-signer".to_string(),
657 policies: RelayerNetworkPolicy::Evm(RelayerEvmPolicy::default()),
658 address: "0x1111".to_string(),
659 notification_id: Some("notification-alpha".to_string()),
660 system_disabled: false,
661 custom_rpc_urls: None,
662 ..Default::default()
663 };
664
665 let relayer2 = RelayerRepoModel {
666 id: "relayer-2".to_string(),
667 name: "Relayer 2".to_string(),
668 network: "polygon".to_string(),
669 paused: true,
670 network_type: NetworkType::Evm,
671 signer_id: "test-signer".to_string(),
672 policies: RelayerNetworkPolicy::Evm(RelayerEvmPolicy::default()),
673 address: "0x2222".to_string(),
674 notification_id: Some("notification-alpha".to_string()), system_disabled: false,
676 custom_rpc_urls: None,
677 ..Default::default()
678 };
679
680 let relayer3 = RelayerRepoModel {
681 id: "relayer-3".to_string(),
682 name: "Relayer 3".to_string(),
683 network: "solana".to_string(),
684 paused: false,
685 network_type: NetworkType::Solana,
686 signer_id: "test-signer".to_string(),
687 policies: RelayerNetworkPolicy::Solana(crate::models::RelayerSolanaPolicy::default()),
688 address: "solana-addr".to_string(),
689 notification_id: Some("notification-beta".to_string()), system_disabled: false,
691 custom_rpc_urls: None,
692 ..Default::default()
693 };
694
695 let relayer4 = RelayerRepoModel {
696 id: "relayer-4".to_string(),
697 name: "Relayer 4".to_string(),
698 network: "stellar".to_string(),
699 paused: false,
700 network_type: NetworkType::Stellar,
701 signer_id: "test-signer".to_string(),
702 policies: RelayerNetworkPolicy::Stellar(crate::models::RelayerStellarPolicy::default()),
703 address: "stellar-addr".to_string(),
704 notification_id: None, system_disabled: true,
706 custom_rpc_urls: None,
707 ..Default::default()
708 };
709
710 let relayer5 = RelayerRepoModel {
711 id: "relayer-5".to_string(),
712 name: "Relayer 5".to_string(),
713 network: "bsc".to_string(),
714 paused: false,
715 network_type: NetworkType::Evm,
716 signer_id: "test-signer".to_string(),
717 policies: RelayerNetworkPolicy::Evm(RelayerEvmPolicy::default()),
718 address: "0x5555".to_string(),
719 notification_id: Some("notification-alpha".to_string()), system_disabled: false,
721 custom_rpc_urls: None,
722 ..Default::default()
723 };
724
725 repo.create(relayer1).await.unwrap();
727 repo.create(relayer2).await.unwrap();
728 repo.create(relayer3).await.unwrap();
729 repo.create(relayer4).await.unwrap();
730 repo.create(relayer5).await.unwrap();
731
732 let relayers_with_alpha = repo
734 .list_by_notification_id("notification-alpha")
735 .await
736 .unwrap();
737 assert_eq!(relayers_with_alpha.len(), 3);
738
739 let alpha_ids: Vec<String> = relayers_with_alpha.iter().map(|r| r.id.clone()).collect();
740 assert!(alpha_ids.contains(&"relayer-1".to_string()));
741 assert!(alpha_ids.contains(&"relayer-2".to_string()));
742 assert!(alpha_ids.contains(&"relayer-5".to_string()));
743 assert!(!alpha_ids.contains(&"relayer-3".to_string()));
744 assert!(!alpha_ids.contains(&"relayer-4".to_string()));
745
746 let relayer2_found = relayers_with_alpha
748 .iter()
749 .find(|r| r.id == "relayer-2")
750 .unwrap();
751 let relayer5_found = relayers_with_alpha
752 .iter()
753 .find(|r| r.id == "relayer-5")
754 .unwrap();
755 assert!(relayer2_found.paused); assert_eq!(relayer5_found.network, "bsc"); let relayers_with_beta = repo
760 .list_by_notification_id("notification-beta")
761 .await
762 .unwrap();
763 assert_eq!(relayers_with_beta.len(), 1);
764 assert_eq!(relayers_with_beta[0].id, "relayer-3");
765 assert_eq!(relayers_with_beta[0].network_type, NetworkType::Solana);
766
767 let relayers_with_gamma = repo
769 .list_by_notification_id("notification-gamma")
770 .await
771 .unwrap();
772 assert_eq!(relayers_with_gamma.len(), 0);
773
774 let relayers_with_empty = repo.list_by_notification_id("").await.unwrap();
776 assert_eq!(relayers_with_empty.len(), 0);
777
778 assert_eq!(repo.count().await.unwrap(), 5);
780
781 repo.delete_by_id("relayer-2".to_string()).await.unwrap();
783
784 let relayers_with_alpha_after_delete = repo
785 .list_by_notification_id("notification-alpha")
786 .await
787 .unwrap();
788 assert_eq!(relayers_with_alpha_after_delete.len(), 2); let alpha_ids_after: Vec<String> = relayers_with_alpha_after_delete
791 .iter()
792 .map(|r| r.id.clone())
793 .collect();
794 assert!(alpha_ids_after.contains(&"relayer-1".to_string()));
795 assert!(!alpha_ids_after.contains(&"relayer-2".to_string())); assert!(alpha_ids_after.contains(&"relayer-5".to_string()));
797
798 let mut updated_relayer = repo.get_by_id("relayer-5".to_string()).await.unwrap();
800 updated_relayer.notification_id = Some("notification-beta".to_string());
801 repo.update("relayer-5".to_string(), updated_relayer)
802 .await
803 .unwrap();
804
805 let relayers_with_alpha_final = repo
807 .list_by_notification_id("notification-alpha")
808 .await
809 .unwrap();
810 assert_eq!(relayers_with_alpha_final.len(), 1);
811 assert_eq!(relayers_with_alpha_final[0].id, "relayer-1");
812
813 let relayers_with_beta_final = repo
815 .list_by_notification_id("notification-beta")
816 .await
817 .unwrap();
818 assert_eq!(relayers_with_beta_final.len(), 2);
819 let beta_ids_final: Vec<String> = relayers_with_beta_final
820 .iter()
821 .map(|r| r.id.clone())
822 .collect();
823 assert!(beta_ids_final.contains(&"relayer-3".to_string()));
824 assert!(beta_ids_final.contains(&"relayer-5".to_string()));
825 }
826}