1use crate::models::PaginationQuery;
11use crate::{
12 models::UpdateRelayerRequest,
13 models::{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 ) -> 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 Ok(relayer.clone())
142 } else {
143 Err(RepositoryError::NotFound(format!(
144 "Relayer with ID {} not found",
145 relayer_id
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 Ok(relayer.clone())
158 } else {
159 Err(RepositoryError::NotFound(format!(
160 "Relayer with ID {} not found",
161 relayer_id
162 )))
163 }
164 }
165}
166
167#[async_trait]
168impl Repository<RelayerRepoModel, String> for InMemoryRelayerRepository {
169 async fn create(&self, relayer: RelayerRepoModel) -> Result<RelayerRepoModel, RepositoryError> {
170 let mut store = Self::acquire_lock(&self.store).await?;
171 if store.contains_key(&relayer.id) {
172 return Err(RepositoryError::ConstraintViolation(format!(
173 "Relayer with ID {} already exists",
174 relayer.id
175 )));
176 }
177 store.insert(relayer.id.clone(), relayer.clone());
178 Ok(relayer)
179 }
180
181 async fn get_by_id(&self, id: String) -> Result<RelayerRepoModel, RepositoryError> {
182 let store = Self::acquire_lock(&self.store).await?;
183 match store.get(&id) {
184 Some(relayer) => Ok(relayer.clone()),
185 None => Err(RepositoryError::NotFound(format!(
186 "Relayer with ID {} not found",
187 id
188 ))),
189 }
190 }
191 #[allow(clippy::map_entry)]
192 async fn update(
193 &self,
194 id: String,
195 relayer: RelayerRepoModel,
196 ) -> Result<RelayerRepoModel, RepositoryError> {
197 let mut store = Self::acquire_lock(&self.store).await?;
198 if store.contains_key(&id) {
199 let mut updated_relayer = relayer;
201 updated_relayer.id = id.clone(); store.insert(id, updated_relayer.clone());
203 Ok(updated_relayer)
204 } else {
205 Err(RepositoryError::NotFound(format!(
206 "Relayer with ID {} not found",
207 id
208 )))
209 }
210 }
211
212 async fn delete_by_id(&self, id: String) -> Result<(), RepositoryError> {
213 let mut store = Self::acquire_lock(&self.store).await?;
214 if store.remove(&id).is_some() {
215 Ok(())
216 } else {
217 Err(RepositoryError::NotFound(format!(
218 "Relayer with ID {} not found",
219 id
220 )))
221 }
222 }
223
224 async fn list_all(&self) -> Result<Vec<RelayerRepoModel>, RepositoryError> {
225 let store = Self::acquire_lock(&self.store).await?;
226 Ok(store.values().cloned().collect())
227 }
228
229 async fn list_paginated(
230 &self,
231 query: PaginationQuery,
232 ) -> Result<PaginatedResult<RelayerRepoModel>, RepositoryError> {
233 let total = self.count().await?;
234 let start = ((query.page - 1) * query.per_page) as usize;
235 let items = self
236 .store
237 .lock()
238 .await
239 .values()
240 .skip(start)
241 .take(query.per_page as usize)
242 .cloned()
243 .collect();
244 Ok(PaginatedResult {
245 items,
246 total: total as u64,
247 page: query.page,
248 per_page: query.per_page,
249 })
250 }
251
252 async fn count(&self) -> Result<usize, RepositoryError> {
253 Ok(self.store.lock().await.len())
254 }
255
256 async fn has_entries(&self) -> Result<bool, RepositoryError> {
257 let store = Self::acquire_lock(&self.store).await?;
258 Ok(!store.is_empty())
259 }
260
261 async fn drop_all_entries(&self) -> Result<(), RepositoryError> {
262 let mut store = Self::acquire_lock(&self.store).await?;
263 store.clear();
264 Ok(())
265 }
266}
267
268#[cfg(test)]
269mod tests {
270 use crate::models::{NetworkType, RelayerEvmPolicy};
271
272 use super::*;
273
274 fn create_test_relayer(id: String) -> RelayerRepoModel {
275 RelayerRepoModel {
276 id: id.clone(),
277 name: format!("Relayer {}", id.clone()),
278 network: "TestNet".to_string(),
279 paused: false,
280 network_type: NetworkType::Evm,
281 policies: RelayerNetworkPolicy::Evm(RelayerEvmPolicy {
282 gas_price_cap: None,
283 whitelist_receivers: None,
284 eip1559_pricing: Some(false),
285 private_transactions: Some(false),
286 min_balance: Some(0),
287 gas_limit_estimation: Some(true),
288 }),
289 signer_id: "test".to_string(),
290 address: "0x".to_string(),
291 notification_id: None,
292 system_disabled: false,
293 custom_rpc_urls: None,
294 }
295 }
296
297 #[actix_web::test]
298 async fn test_new_repository_is_empty() {
299 let repo = InMemoryRelayerRepository::new();
300 assert_eq!(repo.count().await.unwrap(), 0);
301 }
302
303 #[actix_web::test]
304 async fn test_add_relayer() {
305 let repo = InMemoryRelayerRepository::new();
306 let relayer = create_test_relayer("test".to_string());
307
308 repo.create(relayer.clone()).await.unwrap();
309 assert_eq!(repo.count().await.unwrap(), 1);
310
311 let stored = repo.get_by_id("test".to_string()).await.unwrap();
312 assert_eq!(stored.id, relayer.id);
313 assert_eq!(stored.name, relayer.name);
314 }
315
316 #[actix_web::test]
317 async fn test_update_relayer() {
318 let repo = InMemoryRelayerRepository::new();
319 let mut relayer = create_test_relayer("test".to_string());
320
321 repo.create(relayer.clone()).await.unwrap();
322
323 relayer.name = "Updated Name".to_string();
324 repo.update("test".to_string(), relayer.clone())
325 .await
326 .unwrap();
327
328 let updated = repo.get_by_id("test".to_string()).await.unwrap();
329 assert_eq!(updated.name, "Updated Name");
330 }
331
332 #[actix_web::test]
333 async fn test_list_relayers() {
334 let repo = InMemoryRelayerRepository::new();
335 let relayer1 = create_test_relayer("test".to_string());
336 let relayer2 = create_test_relayer("test2".to_string());
337
338 repo.create(relayer1.clone()).await.unwrap();
339 repo.create(relayer2).await.unwrap();
340
341 let relayers = repo.list_all().await.unwrap();
342 assert_eq!(relayers.len(), 2);
343 }
344
345 #[actix_web::test]
346 async fn test_list_active_relayers() {
347 let repo = InMemoryRelayerRepository::new();
348 let relayer1 = create_test_relayer("test".to_string());
349 let mut relayer2 = create_test_relayer("test2".to_string());
350
351 relayer2.paused = true;
352
353 repo.create(relayer1.clone()).await.unwrap();
354 repo.create(relayer2).await.unwrap();
355
356 let active_relayers = repo.list_active().await.unwrap();
357 assert_eq!(active_relayers.len(), 1);
358 assert_eq!(active_relayers[0].id, "test".to_string());
359 }
360
361 #[actix_web::test]
362 async fn test_update_nonexistent_relayer() {
363 let repo = InMemoryRelayerRepository::new();
364 let relayer = create_test_relayer("test".to_string());
365
366 let result = repo.update("test".to_string(), relayer).await;
367 assert!(matches!(result, Err(RepositoryError::NotFound(_))));
368 }
369
370 #[actix_web::test]
371 async fn test_get_nonexistent_relayer() {
372 let repo = InMemoryRelayerRepository::new();
373
374 let result = repo.get_by_id("test".to_string()).await;
375 assert!(matches!(result, Err(RepositoryError::NotFound(_))));
376 }
377
378 #[actix_web::test]
379 async fn test_partial_update_relayer() {
380 let repo = InMemoryRelayerRepository::new();
381
382 let relayer_id = "test_relayer".to_string();
384 let initial_relayer = create_test_relayer(relayer_id.clone());
385
386 repo.create(initial_relayer.clone()).await.unwrap();
387
388 let update_req = UpdateRelayerRequest {
390 name: None,
391 paused: Some(true),
392 policies: None,
393 notification_id: None,
394 custom_rpc_urls: None,
395 };
396
397 let updated_relayer = repo
398 .partial_update(relayer_id.clone(), update_req)
399 .await
400 .unwrap();
401
402 assert_eq!(updated_relayer.id, initial_relayer.id);
403 assert!(updated_relayer.paused);
404 }
405
406 #[actix_web::test]
407 async fn test_disable_relayer() {
408 let repo = InMemoryRelayerRepository::new();
409
410 let relayer_id = "test_relayer".to_string();
412 let initial_relayer = create_test_relayer(relayer_id.clone());
413
414 repo.create(initial_relayer.clone()).await.unwrap();
415
416 let disabled_relayer = repo.disable_relayer(relayer_id.clone()).await.unwrap();
418
419 assert_eq!(disabled_relayer.id, initial_relayer.id);
420 assert!(disabled_relayer.system_disabled);
421 }
422
423 #[actix_web::test]
424 async fn test_enable_relayer() {
425 let repo = InMemoryRelayerRepository::new();
426
427 let relayer_id = "test_relayer".to_string();
429 let mut initial_relayer = create_test_relayer(relayer_id.clone());
430
431 initial_relayer.system_disabled = true;
432
433 repo.create(initial_relayer.clone()).await.unwrap();
434
435 let enabled_relayer = repo.enable_relayer(relayer_id.clone()).await.unwrap();
437
438 assert_eq!(enabled_relayer.id, initial_relayer.id);
439 assert!(!enabled_relayer.system_disabled);
440 }
441
442 #[actix_web::test]
443 async fn test_update_policy() {
444 let repo = InMemoryRelayerRepository::new();
445 let relayer = create_test_relayer("test".to_string());
446
447 repo.create(relayer.clone()).await.unwrap();
448
449 let new_policy = RelayerNetworkPolicy::Evm(RelayerEvmPolicy {
451 gas_price_cap: Some(50000000000),
452 whitelist_receivers: Some(vec!["0x1234".to_string()]),
453 eip1559_pricing: Some(true),
454 private_transactions: Some(true),
455 min_balance: Some(1000000),
456 gas_limit_estimation: Some(true),
457 });
458
459 let updated_relayer = repo
461 .update_policy("test".to_string(), new_policy.clone())
462 .await
463 .unwrap();
464
465 match updated_relayer.policies {
467 RelayerNetworkPolicy::Evm(policy) => {
468 assert_eq!(policy.gas_price_cap, Some(50000000000));
469 assert_eq!(policy.whitelist_receivers, Some(vec!["0x1234".to_string()]));
470 assert_eq!(policy.eip1559_pricing, Some(true));
471 assert!(policy.private_transactions.unwrap_or(false));
472 assert_eq!(policy.min_balance, Some(1000000));
473 }
474 _ => panic!("Unexpected policy type"),
475 }
476 }
477
478 #[actix_web::test]
480 async fn test_has_entries() {
481 let repo = InMemoryRelayerRepository::new();
482 assert!(!repo.has_entries().await.unwrap());
483
484 let relayer = create_test_relayer("test".to_string());
485
486 repo.create(relayer.clone()).await.unwrap();
487 assert!(repo.has_entries().await.unwrap());
488 }
489
490 #[actix_web::test]
491 async fn test_drop_all_entries() {
492 let repo = InMemoryRelayerRepository::new();
493 let relayer = create_test_relayer("test".to_string());
494
495 repo.create(relayer.clone()).await.unwrap();
496
497 assert!(repo.has_entries().await.unwrap());
498
499 repo.drop_all_entries().await.unwrap();
500 assert!(!repo.has_entries().await.unwrap());
501 }
502
503 #[actix_web::test]
504 async fn test_list_by_signer_id() {
505 let repo = InMemoryRelayerRepository::new();
506
507 let relayer1 = RelayerRepoModel {
509 id: "relayer-1".to_string(),
510 name: "Relayer 1".to_string(),
511 network: "ethereum".to_string(),
512 paused: false,
513 network_type: NetworkType::Evm,
514 signer_id: "signer-alpha".to_string(),
515 policies: RelayerNetworkPolicy::Evm(RelayerEvmPolicy::default()),
516 address: "0x1111".to_string(),
517 notification_id: None,
518 system_disabled: false,
519 custom_rpc_urls: None,
520 };
521
522 let relayer2 = RelayerRepoModel {
523 id: "relayer-2".to_string(),
524 name: "Relayer 2".to_string(),
525 network: "polygon".to_string(),
526 paused: true,
527 network_type: NetworkType::Evm,
528 signer_id: "signer-alpha".to_string(), policies: RelayerNetworkPolicy::Evm(RelayerEvmPolicy::default()),
530 address: "0x2222".to_string(),
531 notification_id: None,
532 system_disabled: false,
533 custom_rpc_urls: None,
534 };
535
536 let relayer3 = RelayerRepoModel {
537 id: "relayer-3".to_string(),
538 name: "Relayer 3".to_string(),
539 network: "solana".to_string(),
540 paused: false,
541 network_type: NetworkType::Solana,
542 signer_id: "signer-beta".to_string(), policies: RelayerNetworkPolicy::Solana(crate::models::RelayerSolanaPolicy::default()),
544 address: "solana-addr".to_string(),
545 notification_id: None,
546 system_disabled: false,
547 custom_rpc_urls: None,
548 };
549
550 let relayer4 = RelayerRepoModel {
551 id: "relayer-4".to_string(),
552 name: "Relayer 4".to_string(),
553 network: "stellar".to_string(),
554 paused: false,
555 network_type: NetworkType::Stellar,
556 signer_id: "signer-alpha".to_string(), policies: RelayerNetworkPolicy::Stellar(crate::models::RelayerStellarPolicy::default()),
558 address: "stellar-addr".to_string(),
559 notification_id: Some("notification-1".to_string()),
560 system_disabled: true,
561 custom_rpc_urls: None,
562 };
563
564 repo.create(relayer1).await.unwrap();
566 repo.create(relayer2).await.unwrap();
567 repo.create(relayer3).await.unwrap();
568 repo.create(relayer4).await.unwrap();
569
570 let relayers_with_alpha = repo.list_by_signer_id("signer-alpha").await.unwrap();
572 assert_eq!(relayers_with_alpha.len(), 3);
573
574 let alpha_ids: Vec<String> = relayers_with_alpha.iter().map(|r| r.id.clone()).collect();
575 assert!(alpha_ids.contains(&"relayer-1".to_string()));
576 assert!(alpha_ids.contains(&"relayer-2".to_string()));
577 assert!(alpha_ids.contains(&"relayer-4".to_string()));
578 assert!(!alpha_ids.contains(&"relayer-3".to_string()));
579
580 let relayer2_found = relayers_with_alpha
582 .iter()
583 .find(|r| r.id == "relayer-2")
584 .unwrap();
585 let relayer4_found = relayers_with_alpha
586 .iter()
587 .find(|r| r.id == "relayer-4")
588 .unwrap();
589 assert!(relayer2_found.paused); assert!(relayer4_found.system_disabled); let relayers_with_beta = repo.list_by_signer_id("signer-beta").await.unwrap();
594 assert_eq!(relayers_with_beta.len(), 1);
595 assert_eq!(relayers_with_beta[0].id, "relayer-3");
596 assert_eq!(relayers_with_beta[0].network_type, NetworkType::Solana);
597
598 let relayers_with_gamma = repo.list_by_signer_id("signer-gamma").await.unwrap();
600 assert_eq!(relayers_with_gamma.len(), 0);
601
602 let relayers_with_empty = repo.list_by_signer_id("").await.unwrap();
604 assert_eq!(relayers_with_empty.len(), 0);
605
606 assert_eq!(repo.count().await.unwrap(), 4);
608
609 repo.delete_by_id("relayer-2".to_string()).await.unwrap();
611
612 let relayers_with_alpha_after_delete =
613 repo.list_by_signer_id("signer-alpha").await.unwrap();
614 assert_eq!(relayers_with_alpha_after_delete.len(), 2); let alpha_ids_after: Vec<String> = relayers_with_alpha_after_delete
617 .iter()
618 .map(|r| r.id.clone())
619 .collect();
620 assert!(alpha_ids_after.contains(&"relayer-1".to_string()));
621 assert!(!alpha_ids_after.contains(&"relayer-2".to_string())); assert!(alpha_ids_after.contains(&"relayer-4".to_string()));
623 }
624
625 #[actix_web::test]
626 async fn test_list_by_notification_id() {
627 let repo = InMemoryRelayerRepository::new();
628
629 let relayer1 = RelayerRepoModel {
631 id: "relayer-1".to_string(),
632 name: "Relayer 1".to_string(),
633 network: "ethereum".to_string(),
634 paused: false,
635 network_type: NetworkType::Evm,
636 signer_id: "test-signer".to_string(),
637 policies: RelayerNetworkPolicy::Evm(RelayerEvmPolicy::default()),
638 address: "0x1111".to_string(),
639 notification_id: Some("notification-alpha".to_string()),
640 system_disabled: false,
641 custom_rpc_urls: None,
642 };
643
644 let relayer2 = RelayerRepoModel {
645 id: "relayer-2".to_string(),
646 name: "Relayer 2".to_string(),
647 network: "polygon".to_string(),
648 paused: true,
649 network_type: NetworkType::Evm,
650 signer_id: "test-signer".to_string(),
651 policies: RelayerNetworkPolicy::Evm(RelayerEvmPolicy::default()),
652 address: "0x2222".to_string(),
653 notification_id: Some("notification-alpha".to_string()), system_disabled: false,
655 custom_rpc_urls: None,
656 };
657
658 let relayer3 = RelayerRepoModel {
659 id: "relayer-3".to_string(),
660 name: "Relayer 3".to_string(),
661 network: "solana".to_string(),
662 paused: false,
663 network_type: NetworkType::Solana,
664 signer_id: "test-signer".to_string(),
665 policies: RelayerNetworkPolicy::Solana(crate::models::RelayerSolanaPolicy::default()),
666 address: "solana-addr".to_string(),
667 notification_id: Some("notification-beta".to_string()), system_disabled: false,
669 custom_rpc_urls: None,
670 };
671
672 let relayer4 = RelayerRepoModel {
673 id: "relayer-4".to_string(),
674 name: "Relayer 4".to_string(),
675 network: "stellar".to_string(),
676 paused: false,
677 network_type: NetworkType::Stellar,
678 signer_id: "test-signer".to_string(),
679 policies: RelayerNetworkPolicy::Stellar(crate::models::RelayerStellarPolicy::default()),
680 address: "stellar-addr".to_string(),
681 notification_id: None, system_disabled: true,
683 custom_rpc_urls: None,
684 };
685
686 let relayer5 = RelayerRepoModel {
687 id: "relayer-5".to_string(),
688 name: "Relayer 5".to_string(),
689 network: "bsc".to_string(),
690 paused: false,
691 network_type: NetworkType::Evm,
692 signer_id: "test-signer".to_string(),
693 policies: RelayerNetworkPolicy::Evm(RelayerEvmPolicy::default()),
694 address: "0x5555".to_string(),
695 notification_id: Some("notification-alpha".to_string()), system_disabled: false,
697 custom_rpc_urls: None,
698 };
699
700 repo.create(relayer1).await.unwrap();
702 repo.create(relayer2).await.unwrap();
703 repo.create(relayer3).await.unwrap();
704 repo.create(relayer4).await.unwrap();
705 repo.create(relayer5).await.unwrap();
706
707 let relayers_with_alpha = repo
709 .list_by_notification_id("notification-alpha")
710 .await
711 .unwrap();
712 assert_eq!(relayers_with_alpha.len(), 3);
713
714 let alpha_ids: Vec<String> = relayers_with_alpha.iter().map(|r| r.id.clone()).collect();
715 assert!(alpha_ids.contains(&"relayer-1".to_string()));
716 assert!(alpha_ids.contains(&"relayer-2".to_string()));
717 assert!(alpha_ids.contains(&"relayer-5".to_string()));
718 assert!(!alpha_ids.contains(&"relayer-3".to_string()));
719 assert!(!alpha_ids.contains(&"relayer-4".to_string()));
720
721 let relayer2_found = relayers_with_alpha
723 .iter()
724 .find(|r| r.id == "relayer-2")
725 .unwrap();
726 let relayer5_found = relayers_with_alpha
727 .iter()
728 .find(|r| r.id == "relayer-5")
729 .unwrap();
730 assert!(relayer2_found.paused); assert_eq!(relayer5_found.network, "bsc"); let relayers_with_beta = repo
735 .list_by_notification_id("notification-beta")
736 .await
737 .unwrap();
738 assert_eq!(relayers_with_beta.len(), 1);
739 assert_eq!(relayers_with_beta[0].id, "relayer-3");
740 assert_eq!(relayers_with_beta[0].network_type, NetworkType::Solana);
741
742 let relayers_with_gamma = repo
744 .list_by_notification_id("notification-gamma")
745 .await
746 .unwrap();
747 assert_eq!(relayers_with_gamma.len(), 0);
748
749 let relayers_with_empty = repo.list_by_notification_id("").await.unwrap();
751 assert_eq!(relayers_with_empty.len(), 0);
752
753 assert_eq!(repo.count().await.unwrap(), 5);
755
756 repo.delete_by_id("relayer-2".to_string()).await.unwrap();
758
759 let relayers_with_alpha_after_delete = repo
760 .list_by_notification_id("notification-alpha")
761 .await
762 .unwrap();
763 assert_eq!(relayers_with_alpha_after_delete.len(), 2); let alpha_ids_after: Vec<String> = relayers_with_alpha_after_delete
766 .iter()
767 .map(|r| r.id.clone())
768 .collect();
769 assert!(alpha_ids_after.contains(&"relayer-1".to_string()));
770 assert!(!alpha_ids_after.contains(&"relayer-2".to_string())); assert!(alpha_ids_after.contains(&"relayer-5".to_string()));
772
773 let mut updated_relayer = repo.get_by_id("relayer-5".to_string()).await.unwrap();
775 updated_relayer.notification_id = Some("notification-beta".to_string());
776 repo.update("relayer-5".to_string(), updated_relayer)
777 .await
778 .unwrap();
779
780 let relayers_with_alpha_final = repo
782 .list_by_notification_id("notification-alpha")
783 .await
784 .unwrap();
785 assert_eq!(relayers_with_alpha_final.len(), 1);
786 assert_eq!(relayers_with_alpha_final[0].id, "relayer-1");
787
788 let relayers_with_beta_final = repo
790 .list_by_notification_id("notification-beta")
791 .await
792 .unwrap();
793 assert_eq!(relayers_with_beta_final.len(), 2);
794 let beta_ids_final: Vec<String> = relayers_with_beta_final
795 .iter()
796 .map(|r| r.id.clone())
797 .collect();
798 assert!(beta_ids_final.contains(&"relayer-3".to_string()));
799 assert!(beta_ids_final.contains(&"relayer-5".to_string()));
800 }
801}