1use crate::domain::{
9 get_network_relayer, get_relayer_by_id, get_transaction_by_id, Relayer, SignTransactionRequest,
10};
11use crate::jobs::JobProducerTrait;
12use crate::models::{
13 AppState, NetworkRepoModel, NetworkTransactionRequest, NotificationRepoModel, RelayerRepoModel,
14 SignerRepoModel, ThinDataAppState, TransactionRepoModel, TransactionResponse,
15};
16use crate::repositories::{
17 NetworkRepository, PluginRepositoryTrait, RelayerRepository, Repository,
18 TransactionCounterTrait, TransactionRepository,
19};
20use actix_web::web;
21use async_trait::async_trait;
22use serde::{Deserialize, Serialize};
23use strum::Display;
24
25use super::PluginError;
26
27#[cfg(test)]
28use mockall::automock;
29
30#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Display)]
31pub enum PluginMethod {
32 #[serde(rename = "sendTransaction")]
33 SendTransaction,
34 #[serde(rename = "getTransaction")]
35 GetTransaction,
36 #[serde(rename = "getRelayerStatus")]
37 GetRelayerStatus,
38 #[serde(rename = "signTransaction")]
39 SignTransaction,
40 #[serde(rename = "getRelayer")]
41 GetRelayer,
42}
43
44#[derive(Deserialize, Serialize, Clone, Debug)]
45#[serde(rename_all = "camelCase")]
46pub struct Request {
47 pub request_id: String,
48 pub relayer_id: String,
49 pub method: PluginMethod,
50 pub payload: serde_json::Value,
51}
52
53#[derive(Deserialize, Serialize, Clone, Debug)]
54#[serde(rename_all = "camelCase")]
55pub struct GetTransactionRequest {
56 pub transaction_id: String,
57}
58
59#[derive(Serialize, Deserialize, Clone, Debug, Default)]
60#[serde(rename_all = "camelCase")]
61pub struct Response {
62 pub request_id: String,
63 pub result: Option<serde_json::Value>,
64 pub error: Option<String>,
65}
66
67#[async_trait]
68#[cfg_attr(test, automock)]
69pub trait RelayerApiTrait<J, RR, TR, NR, NFR, SR, TCR, PR>: Send + Sync
70where
71 J: JobProducerTrait + 'static,
72 TR: TransactionRepository + Repository<TransactionRepoModel, String> + Send + Sync + 'static,
73 RR: RelayerRepository + Repository<RelayerRepoModel, String> + Send + Sync + 'static,
74 NR: NetworkRepository + Repository<NetworkRepoModel, String> + Send + Sync + 'static,
75 NFR: Repository<NotificationRepoModel, String> + Send + Sync + 'static,
76 SR: Repository<SignerRepoModel, String> + Send + Sync + 'static,
77 TCR: TransactionCounterTrait + Send + Sync + 'static,
78 PR: PluginRepositoryTrait + Send + Sync + 'static,
79{
80 async fn handle_request(
81 &self,
82 request: Request,
83 state: &web::ThinData<AppState<J, RR, TR, NR, NFR, SR, TCR, PR>>,
84 ) -> Response;
85
86 async fn process_request(
87 &self,
88 request: Request,
89 state: &web::ThinData<AppState<J, RR, TR, NR, NFR, SR, TCR, PR>>,
90 ) -> Result<Response, PluginError>;
91
92 async fn handle_send_transaction(
93 &self,
94 request: Request,
95 state: &web::ThinData<AppState<J, RR, TR, NR, NFR, SR, TCR, PR>>,
96 ) -> Result<Response, PluginError>;
97
98 async fn handle_get_transaction(
99 &self,
100 request: Request,
101 state: &web::ThinData<AppState<J, RR, TR, NR, NFR, SR, TCR, PR>>,
102 ) -> Result<Response, PluginError>;
103
104 async fn handle_get_relayer_status(
105 &self,
106 request: Request,
107 state: &web::ThinData<AppState<J, RR, TR, NR, NFR, SR, TCR, PR>>,
108 ) -> Result<Response, PluginError>;
109
110 async fn handle_sign_transaction(
111 &self,
112 request: Request,
113 state: &web::ThinData<AppState<J, RR, TR, NR, NFR, SR, TCR, PR>>,
114 ) -> Result<Response, PluginError>;
115 async fn handle_get_relayer_info(
116 &self,
117 request: Request,
118 state: &web::ThinData<AppState<J, RR, TR, NR, NFR, SR, TCR, PR>>,
119 ) -> Result<Response, PluginError>;
120}
121
122#[derive(Default)]
123pub struct RelayerApi;
124
125impl RelayerApi {
126 pub async fn handle_request<J, RR, TR, NR, NFR, SR, TCR, PR>(
127 &self,
128 request: Request,
129 state: &ThinDataAppState<J, RR, TR, NR, NFR, SR, TCR, PR>,
130 ) -> Response
131 where
132 J: JobProducerTrait + 'static,
133 TR: TransactionRepository
134 + Repository<TransactionRepoModel, String>
135 + Send
136 + Sync
137 + 'static,
138 RR: RelayerRepository + Repository<RelayerRepoModel, String> + Send + Sync + 'static,
139 NR: NetworkRepository + Repository<NetworkRepoModel, String> + Send + Sync + 'static,
140 NFR: Repository<NotificationRepoModel, String> + Send + Sync + 'static,
141 SR: Repository<SignerRepoModel, String> + Send + Sync + 'static,
142 TCR: TransactionCounterTrait + Send + Sync + 'static,
143 PR: PluginRepositoryTrait + Send + Sync + 'static,
144 {
145 match self.process_request(request.clone(), state).await {
146 Ok(response) => response,
147 Err(e) => Response {
148 request_id: request.request_id,
149 result: None,
150 error: Some(e.to_string()),
151 },
152 }
153 }
154
155 async fn process_request<J, RR, TR, NR, NFR, SR, TCR, PR>(
156 &self,
157 request: Request,
158 state: &ThinDataAppState<J, RR, TR, NR, NFR, SR, TCR, PR>,
159 ) -> Result<Response, PluginError>
160 where
161 J: JobProducerTrait + 'static,
162 TR: TransactionRepository
163 + Repository<TransactionRepoModel, String>
164 + Send
165 + Sync
166 + 'static,
167 RR: RelayerRepository + Repository<RelayerRepoModel, String> + Send + Sync + 'static,
168 NR: NetworkRepository + Repository<NetworkRepoModel, String> + Send + Sync + 'static,
169 NFR: Repository<NotificationRepoModel, String> + Send + Sync + 'static,
170 SR: Repository<SignerRepoModel, String> + Send + Sync + 'static,
171 TCR: TransactionCounterTrait + Send + Sync + 'static,
172 PR: PluginRepositoryTrait + Send + Sync + 'static,
173 {
174 match request.method {
175 PluginMethod::SendTransaction => self.handle_send_transaction(request, state).await,
176 PluginMethod::GetTransaction => self.handle_get_transaction(request, state).await,
177 PluginMethod::GetRelayerStatus => self.handle_get_relayer_status(request, state).await,
178 PluginMethod::SignTransaction => self.handle_sign_transaction(request, state).await,
179 PluginMethod::GetRelayer => self.handle_get_relayer_info(request, state).await,
180 }
181 }
182
183 async fn handle_send_transaction<J, RR, TR, NR, NFR, SR, TCR, PR>(
184 &self,
185 request: Request,
186 state: &ThinDataAppState<J, RR, TR, NR, NFR, SR, TCR, PR>,
187 ) -> Result<Response, PluginError>
188 where
189 J: JobProducerTrait + 'static,
190 TR: TransactionRepository
191 + Repository<TransactionRepoModel, String>
192 + Send
193 + Sync
194 + 'static,
195 RR: RelayerRepository + Repository<RelayerRepoModel, String> + Send + Sync + 'static,
196 NR: NetworkRepository + Repository<NetworkRepoModel, String> + Send + Sync + 'static,
197 NFR: Repository<NotificationRepoModel, String> + Send + Sync + 'static,
198 SR: Repository<SignerRepoModel, String> + Send + Sync + 'static,
199 TCR: TransactionCounterTrait + Send + Sync + 'static,
200 PR: PluginRepositoryTrait + Send + Sync + 'static,
201 {
202 let relayer_repo_model = get_relayer_by_id(request.relayer_id.clone(), state)
203 .await
204 .map_err(|e| PluginError::RelayerError(e.to_string()))?;
205
206 relayer_repo_model
207 .validate_active_state()
208 .map_err(|e| PluginError::RelayerError(e.to_string()))?;
209
210 let network_relayer = get_network_relayer(request.relayer_id.clone(), state)
211 .await
212 .map_err(|e| PluginError::RelayerError(e.to_string()))?;
213
214 let tx_request = NetworkTransactionRequest::from_json(
215 &relayer_repo_model.network_type,
216 request.payload.clone(),
217 )
218 .map_err(|e| PluginError::RelayerError(e.to_string()))?;
219
220 tx_request
221 .validate(&relayer_repo_model)
222 .map_err(|e| PluginError::RelayerError(e.to_string()))?;
223
224 let transaction = network_relayer
225 .process_transaction_request(tx_request)
226 .await
227 .map_err(|e| PluginError::RelayerError(e.to_string()))?;
228
229 let transaction_response: TransactionResponse = transaction.into();
230 let result = serde_json::to_value(transaction_response)
231 .map_err(|e| PluginError::RelayerError(e.to_string()))?;
232
233 Ok(Response {
234 request_id: request.request_id,
235 result: Some(result),
236 error: None,
237 })
238 }
239
240 async fn handle_get_transaction<J, RR, TR, NR, NFR, SR, TCR, PR>(
241 &self,
242 request: Request,
243 state: &ThinDataAppState<J, RR, TR, NR, NFR, SR, TCR, PR>,
244 ) -> Result<Response, PluginError>
245 where
246 J: JobProducerTrait + 'static,
247 TR: TransactionRepository
248 + Repository<TransactionRepoModel, String>
249 + Send
250 + Sync
251 + 'static,
252 RR: RelayerRepository + Repository<RelayerRepoModel, String> + Send + Sync + 'static,
253 NR: NetworkRepository + Repository<NetworkRepoModel, String> + Send + Sync + 'static,
254 NFR: Repository<NotificationRepoModel, String> + Send + Sync + 'static,
255 SR: Repository<SignerRepoModel, String> + Send + Sync + 'static,
256 TCR: TransactionCounterTrait + Send + Sync + 'static,
257 PR: PluginRepositoryTrait + Send + Sync + 'static,
258 {
259 get_relayer_by_id(request.relayer_id.clone(), state)
261 .await
262 .map_err(|e| PluginError::RelayerError(e.to_string()))?;
263
264 let get_transaction_request: GetTransactionRequest =
265 serde_json::from_value(request.payload)
266 .map_err(|e| PluginError::InvalidPayload(e.to_string()))?;
267
268 let transaction = get_transaction_by_id(get_transaction_request.transaction_id, state)
269 .await
270 .map_err(|e| PluginError::RelayerError(e.to_string()))?;
271
272 let transaction_response: TransactionResponse = transaction.into();
273
274 let result = serde_json::to_value(transaction_response)
275 .map_err(|e| PluginError::RelayerError(e.to_string()))?;
276
277 Ok(Response {
278 request_id: request.request_id,
279 result: Some(result),
280 error: None,
281 })
282 }
283
284 async fn handle_get_relayer_status<J, RR, TR, NR, NFR, SR, TCR, PR>(
285 &self,
286 request: Request,
287 state: &ThinDataAppState<J, RR, TR, NR, NFR, SR, TCR, PR>,
288 ) -> Result<Response, PluginError>
289 where
290 J: JobProducerTrait + 'static,
291 TR: TransactionRepository
292 + Repository<TransactionRepoModel, String>
293 + Send
294 + Sync
295 + 'static,
296 RR: RelayerRepository + Repository<RelayerRepoModel, String> + Send + Sync + 'static,
297 NR: NetworkRepository + Repository<NetworkRepoModel, String> + Send + Sync + 'static,
298 NFR: Repository<NotificationRepoModel, String> + Send + Sync + 'static,
299 SR: Repository<SignerRepoModel, String> + Send + Sync + 'static,
300 TCR: TransactionCounterTrait + Send + Sync + 'static,
301 PR: PluginRepositoryTrait + Send + Sync + 'static,
302 {
303 let network_relayer = get_network_relayer(request.relayer_id.clone(), state)
304 .await
305 .map_err(|e| PluginError::RelayerError(e.to_string()))?;
306
307 let status = network_relayer
308 .get_status()
309 .await
310 .map_err(|e| PluginError::RelayerError(e.to_string()))?;
311
312 let result =
313 serde_json::to_value(status).map_err(|e| PluginError::RelayerError(e.to_string()))?;
314
315 Ok(Response {
316 request_id: request.request_id,
317 result: Some(result),
318 error: None,
319 })
320 }
321
322 async fn handle_sign_transaction<J, RR, TR, NR, NFR, SR, TCR, PR>(
323 &self,
324 request: Request,
325 state: &ThinDataAppState<J, RR, TR, NR, NFR, SR, TCR, PR>,
326 ) -> Result<Response, PluginError>
327 where
328 J: JobProducerTrait + 'static,
329 TR: TransactionRepository
330 + Repository<TransactionRepoModel, String>
331 + Send
332 + Sync
333 + 'static,
334 RR: RelayerRepository + Repository<RelayerRepoModel, String> + Send + Sync + 'static,
335 NR: NetworkRepository + Repository<NetworkRepoModel, String> + Send + Sync + 'static,
336 NFR: Repository<NotificationRepoModel, String> + Send + Sync + 'static,
337 SR: Repository<SignerRepoModel, String> + Send + Sync + 'static,
338 TCR: TransactionCounterTrait + Send + Sync + 'static,
339 PR: PluginRepositoryTrait + Send + Sync + 'static,
340 {
341 let sign_request: SignTransactionRequest = serde_json::from_value(request.payload)
342 .map_err(|e| PluginError::InvalidPayload(e.to_string()))?;
343
344 let network_relayer = get_network_relayer(request.relayer_id.clone(), state)
345 .await
346 .map_err(|e| PluginError::RelayerError(e.to_string()))?;
347
348 let response = network_relayer
349 .sign_transaction(&sign_request)
350 .await
351 .map_err(|e| PluginError::RelayerError(e.to_string()))?;
352
353 let result =
354 serde_json::to_value(response).map_err(|e| PluginError::RelayerError(e.to_string()))?;
355
356 Ok(Response {
357 request_id: request.request_id,
358 result: Some(result),
359 error: None,
360 })
361 }
362
363 async fn handle_get_relayer_info<J, RR, TR, NR, NFR, SR, TCR, PR>(
364 &self,
365 request: Request,
366 state: &ThinDataAppState<J, RR, TR, NR, NFR, SR, TCR, PR>,
367 ) -> Result<Response, PluginError>
368 where
369 J: JobProducerTrait + 'static,
370 TR: TransactionRepository
371 + Repository<TransactionRepoModel, String>
372 + Send
373 + Sync
374 + 'static,
375 RR: RelayerRepository + Repository<RelayerRepoModel, String> + Send + Sync + 'static,
376 NR: NetworkRepository + Repository<NetworkRepoModel, String> + Send + Sync + 'static,
377 NFR: Repository<NotificationRepoModel, String> + Send + Sync + 'static,
378 SR: Repository<SignerRepoModel, String> + Send + Sync + 'static,
379 TCR: TransactionCounterTrait + Send + Sync + 'static,
380 PR: PluginRepositoryTrait + Send + Sync + 'static,
381 {
382 let relayer = get_relayer_by_id(request.relayer_id.clone(), state)
383 .await
384 .map_err(|e| PluginError::RelayerError(e.to_string()))?;
385 let relayer_response: crate::models::RelayerResponse = relayer.into();
386 let result = serde_json::to_value(relayer_response)
387 .map_err(|e| PluginError::RelayerError(e.to_string()))?;
388 Ok(Response {
389 request_id: request.request_id,
390 result: Some(result),
391 error: None,
392 })
393 }
394}
395
396#[async_trait]
397impl<J, RR, TR, NR, NFR, SR, TCR, PR> RelayerApiTrait<J, RR, TR, NR, NFR, SR, TCR, PR>
398 for RelayerApi
399where
400 J: JobProducerTrait + 'static,
401 TR: TransactionRepository + Repository<TransactionRepoModel, String> + Send + Sync + 'static,
402 RR: RelayerRepository + Repository<RelayerRepoModel, String> + Send + Sync + 'static,
403 NR: NetworkRepository + Repository<NetworkRepoModel, String> + Send + Sync + 'static,
404 NFR: Repository<NotificationRepoModel, String> + Send + Sync + 'static,
405 SR: Repository<SignerRepoModel, String> + Send + Sync + 'static,
406 TCR: TransactionCounterTrait + Send + Sync + 'static,
407 PR: PluginRepositoryTrait + Send + Sync + 'static,
408{
409 async fn handle_request(
410 &self,
411 request: Request,
412 state: &ThinDataAppState<J, RR, TR, NR, NFR, SR, TCR, PR>,
413 ) -> Response {
414 self.handle_request(request, state).await
415 }
416
417 async fn process_request(
418 &self,
419 request: Request,
420 state: &ThinDataAppState<J, RR, TR, NR, NFR, SR, TCR, PR>,
421 ) -> Result<Response, PluginError> {
422 self.process_request(request, state).await
423 }
424
425 async fn handle_send_transaction(
426 &self,
427 request: Request,
428 state: &ThinDataAppState<J, RR, TR, NR, NFR, SR, TCR, PR>,
429 ) -> Result<Response, PluginError> {
430 self.handle_send_transaction(request, state).await
431 }
432
433 async fn handle_get_transaction(
434 &self,
435 request: Request,
436 state: &ThinDataAppState<J, RR, TR, NR, NFR, SR, TCR, PR>,
437 ) -> Result<Response, PluginError> {
438 self.handle_get_transaction(request, state).await
439 }
440
441 async fn handle_get_relayer_status(
442 &self,
443 request: Request,
444 state: &ThinDataAppState<J, RR, TR, NR, NFR, SR, TCR, PR>,
445 ) -> Result<Response, PluginError> {
446 self.handle_get_relayer_status(request, state).await
447 }
448
449 async fn handle_sign_transaction(
450 &self,
451 request: Request,
452 state: &ThinDataAppState<J, RR, TR, NR, NFR, SR, TCR, PR>,
453 ) -> Result<Response, PluginError> {
454 self.handle_sign_transaction(request, state).await
455 }
456
457 async fn handle_get_relayer_info(
458 &self,
459 request: Request,
460 state: &ThinDataAppState<J, RR, TR, NR, NFR, SR, TCR, PR>,
461 ) -> Result<Response, PluginError> {
462 self.handle_get_relayer_info(request, state).await
463 }
464}
465
466#[cfg(test)]
467mod tests {
468 use std::env;
469
470 use crate::utils::mocks::mockutils::{
471 create_mock_app_state, create_mock_evm_transaction_request, create_mock_network,
472 create_mock_relayer, create_mock_signer, create_mock_transaction,
473 };
474
475 use super::*;
476
477 fn setup_test_env() {
478 env::set_var("API_KEY", "7EF1CB7C-5003-4696-B384-C72AF8C3E15D"); env::set_var("REDIS_URL", "redis://localhost:6379");
480 env::set_var("RPC_TIMEOUT_MS", "5000");
481 }
482
483 #[tokio::test]
484 async fn test_handle_request() {
485 setup_test_env();
486 let state = create_mock_app_state(
487 Some(vec![create_mock_relayer("test".to_string(), false)]),
488 Some(vec![create_mock_signer()]),
489 Some(vec![create_mock_network()]),
490 None,
491 None,
492 )
493 .await;
494
495 let request = Request {
496 request_id: "test".to_string(),
497 relayer_id: "test".to_string(),
498 method: PluginMethod::SendTransaction,
499 payload: serde_json::json!(create_mock_evm_transaction_request()),
500 };
501
502 let relayer_api = RelayerApi;
503 let response = relayer_api
504 .handle_request(request.clone(), &web::ThinData(state))
505 .await;
506
507 assert!(response.error.is_none());
508 assert!(response.result.is_some());
509 }
510
511 #[tokio::test]
512 async fn test_handle_request_error_paused_relayer() {
513 setup_test_env();
514 let paused = true;
515 let state = create_mock_app_state(
516 Some(vec![create_mock_relayer("test".to_string(), paused)]),
517 Some(vec![create_mock_signer()]),
518 Some(vec![create_mock_network()]),
519 None,
520 None,
521 )
522 .await;
523
524 let request = Request {
525 request_id: "test".to_string(),
526 relayer_id: "test".to_string(),
527 method: PluginMethod::SendTransaction,
528 payload: serde_json::json!(create_mock_evm_transaction_request()),
529 };
530
531 let relayer_api = RelayerApi;
532 let response = relayer_api
533 .handle_request(request.clone(), &web::ThinData(state))
534 .await;
535
536 assert!(response.error.is_some());
537 assert!(response.result.is_none());
538 assert_eq!(response.error.unwrap(), "Relayer error: Relayer is paused");
539 }
540
541 #[tokio::test]
542 async fn test_handle_request_using_trait() {
543 setup_test_env();
544 let state = create_mock_app_state(
545 Some(vec![create_mock_relayer("test".to_string(), false)]),
546 Some(vec![create_mock_signer()]),
547 Some(vec![create_mock_network()]),
548 None,
549 None,
550 )
551 .await;
552
553 let request = Request {
554 request_id: "test".to_string(),
555 relayer_id: "test".to_string(),
556 method: PluginMethod::SendTransaction,
557 payload: serde_json::json!(create_mock_evm_transaction_request()),
558 };
559
560 let relayer_api = RelayerApi;
561
562 let state = web::ThinData(state);
563
564 let response = RelayerApiTrait::handle_request(&relayer_api, request.clone(), &state).await;
565
566 assert!(response.error.is_none());
567 assert!(response.result.is_some());
568
569 let response =
570 RelayerApiTrait::process_request(&relayer_api, request.clone(), &state).await;
571
572 assert!(response.is_ok());
573
574 let response =
575 RelayerApiTrait::handle_send_transaction(&relayer_api, request.clone(), &state).await;
576
577 assert!(response.is_ok());
578 }
579
580 #[tokio::test]
581 async fn test_handle_get_transaction() {
582 setup_test_env();
583 let state = create_mock_app_state(
584 Some(vec![create_mock_relayer("test".to_string(), false)]),
585 Some(vec![create_mock_signer()]),
586 Some(vec![create_mock_network()]),
587 None,
588 Some(vec![create_mock_transaction()]),
589 )
590 .await;
591
592 let request = Request {
593 request_id: "test".to_string(),
594 relayer_id: "test".to_string(),
595 method: PluginMethod::GetTransaction,
596 payload: serde_json::json!(GetTransactionRequest {
597 transaction_id: "test".to_string(),
598 }),
599 };
600
601 let relayer_api = RelayerApi;
602 let response = relayer_api
603 .handle_request(request.clone(), &web::ThinData(state))
604 .await;
605
606 assert!(response.error.is_none());
607 assert!(response.result.is_some());
608 }
609
610 #[tokio::test]
611 async fn test_handle_get_transaction_error_relayer_not_found() {
612 setup_test_env();
613 let state = create_mock_app_state(
614 None,
615 Some(vec![create_mock_signer()]),
616 Some(vec![create_mock_network()]),
617 None,
618 Some(vec![create_mock_transaction()]),
619 )
620 .await;
621
622 let request = Request {
623 request_id: "test".to_string(),
624 relayer_id: "test".to_string(),
625 method: PluginMethod::GetTransaction,
626 payload: serde_json::json!(GetTransactionRequest {
627 transaction_id: "test".to_string(),
628 }),
629 };
630
631 let relayer_api = RelayerApi;
632 let response = relayer_api
633 .handle_request(request.clone(), &web::ThinData(state))
634 .await;
635
636 assert!(response.error.is_some());
637 let error = response.error.unwrap();
638 assert!(error.contains("Relayer with ID test not found"));
639 }
640
641 #[tokio::test]
642 async fn test_handle_get_transaction_error_transaction_not_found() {
643 setup_test_env();
644 let state = create_mock_app_state(
645 Some(vec![create_mock_relayer("test".to_string(), false)]),
646 Some(vec![create_mock_signer()]),
647 Some(vec![create_mock_network()]),
648 None,
649 None,
650 )
651 .await;
652
653 let request = Request {
654 request_id: "test".to_string(),
655 relayer_id: "test".to_string(),
656 method: PluginMethod::GetTransaction,
657 payload: serde_json::json!(GetTransactionRequest {
658 transaction_id: "test".to_string(),
659 }),
660 };
661
662 let relayer_api = RelayerApi;
663 let response = relayer_api
664 .handle_request(request.clone(), &web::ThinData(state))
665 .await;
666
667 assert!(response.error.is_some());
668 let error = response.error.unwrap();
669 assert!(error.contains("Transaction with ID test not found"));
670 }
671
672 #[tokio::test]
673 async fn test_handle_get_relayer_status_relayer_not_found() {
674 setup_test_env();
675 let state = create_mock_app_state(
676 None,
677 Some(vec![create_mock_signer()]),
678 Some(vec![create_mock_network()]),
679 None,
680 None,
681 )
682 .await;
683
684 let request = Request {
685 request_id: "test".to_string(),
686 relayer_id: "test".to_string(),
687 method: PluginMethod::GetRelayerStatus,
688 payload: serde_json::json!({}),
689 };
690
691 let relayer_api = RelayerApi;
692 let response = relayer_api
693 .handle_request(request.clone(), &web::ThinData(state))
694 .await;
695
696 assert!(response.error.is_some());
697 let error = response.error.unwrap();
698 assert!(error.contains("Relayer with ID test not found"));
699 }
700
701 #[tokio::test]
702 async fn test_handle_sign_transaction_evm_not_supported() {
703 setup_test_env();
704 let state = create_mock_app_state(
705 Some(vec![create_mock_relayer("test".to_string(), false)]),
706 Some(vec![create_mock_signer()]),
707 Some(vec![create_mock_network()]),
708 None,
709 None,
710 )
711 .await;
712
713 let request = Request {
714 request_id: "test".to_string(),
715 relayer_id: "test".to_string(),
716 method: PluginMethod::SignTransaction,
717 payload: serde_json::json!({
718 "unsigned_xdr": "test_xdr"
719 }),
720 };
721
722 let relayer_api = RelayerApi;
723 let response = relayer_api
724 .handle_request(request.clone(), &web::ThinData(state))
725 .await;
726
727 assert!(response.error.is_some());
728 let error = response.error.unwrap();
729 assert!(error.contains("sign_transaction not supported for EVM"));
730 }
731
732 #[tokio::test]
733 async fn test_handle_sign_transaction_invalid_payload() {
734 setup_test_env();
735 let state = create_mock_app_state(
736 Some(vec![create_mock_relayer("test".to_string(), false)]),
737 Some(vec![create_mock_signer()]),
738 Some(vec![create_mock_network()]),
739 None,
740 None,
741 )
742 .await;
743
744 let request = Request {
745 request_id: "test".to_string(),
746 relayer_id: "test".to_string(),
747 method: PluginMethod::SignTransaction,
748 payload: serde_json::json!({"invalid": "payload"}),
749 };
750
751 let relayer_api = RelayerApi;
752 let response = relayer_api
753 .handle_request(request.clone(), &web::ThinData(state))
754 .await;
755
756 assert!(response.error.is_some());
757 let error = response.error.unwrap();
758 assert!(error.contains("Invalid payload"));
759 }
760
761 #[tokio::test]
762 async fn test_handle_sign_transaction_relayer_not_found() {
763 setup_test_env();
764 let state = create_mock_app_state(
765 None,
766 Some(vec![create_mock_signer()]),
767 Some(vec![create_mock_network()]),
768 None,
769 None,
770 )
771 .await;
772
773 let request = Request {
774 request_id: "test".to_string(),
775 relayer_id: "test".to_string(),
776 method: PluginMethod::SignTransaction,
777 payload: serde_json::json!({
778 "unsigned_xdr": "test_xdr"
779 }),
780 };
781
782 let relayer_api = RelayerApi;
783 let response = relayer_api
784 .handle_request(request.clone(), &web::ThinData(state))
785 .await;
786
787 assert!(response.error.is_some());
788 let error = response.error.unwrap();
789 assert!(error.contains("Relayer with ID test not found"));
790 }
791
792 #[tokio::test]
793 async fn test_handle_get_relayer_info_success() {
794 setup_test_env();
795 let state = create_mock_app_state(
796 Some(vec![create_mock_relayer("test".to_string(), false)]),
797 Some(vec![create_mock_signer()]),
798 Some(vec![create_mock_network()]),
799 None,
800 None,
801 )
802 .await;
803
804 let request = Request {
805 request_id: "test".to_string(),
806 relayer_id: "test".to_string(),
807 method: PluginMethod::GetRelayer,
808 payload: serde_json::json!({}),
809 };
810
811 let relayer_api = RelayerApi;
812 let response = relayer_api
813 .handle_request(request.clone(), &web::ThinData(state))
814 .await;
815
816 assert!(response.error.is_none());
817 assert!(response.result.is_some());
818
819 let result = response.result.unwrap();
820 assert!(result.get("id").is_some());
821 assert!(result.get("name").is_some());
822 assert!(result.get("network").is_some());
823 assert!(result.get("address").is_some());
824 }
825
826 #[tokio::test]
827 async fn test_handle_get_relayer_info_relayer_not_found() {
828 setup_test_env();
829 let state = create_mock_app_state(
830 None,
831 Some(vec![create_mock_signer()]),
832 Some(vec![create_mock_network()]),
833 None,
834 None,
835 )
836 .await;
837
838 let request = Request {
839 request_id: "test".to_string(),
840 relayer_id: "test".to_string(),
841 method: PluginMethod::GetRelayer,
842 payload: serde_json::json!({}),
843 };
844
845 let relayer_api = RelayerApi;
846 let response = relayer_api
847 .handle_request(request.clone(), &web::ThinData(state))
848 .await;
849
850 assert!(response.error.is_some());
851 let error = response.error.unwrap();
852 assert!(error.contains("Relayer with ID test not found"));
853 }
854}