1use serde::{Deserialize, Serialize};
10use validator::Validate;
11
12use crate::{
13 messages::send_transaction::SendTransactionConfig,
14 validation::{
15 validate_airdrop_amount_i64, validate_base58, validate_pubkey, validate_signature,
16 validate_transaction_data,
17 },
18 GetBlockRequest, GetSignatureStatusesRequest,
19};
20
21#[derive(Debug, Deserialize, Serialize, Clone, Validate)]
23pub struct GetSubscriptionsRequest {
24 #[validate(length(min = 1, message = "Subscriber pubkey cannot be empty"))]
25 #[validate(custom(function = validate_pubkey))]
26 pub subscriber: String,
27
28 #[serde(skip_serializing_if = "Option::is_none")]
29 pub nonce: Option<String>,
30
31 #[serde(skip_serializing_if = "Option::is_none")]
32 pub limit: Option<u64>,
33}
34
35impl GetSubscriptionsRequest {
36 pub fn new(subscriber: String, nonce: Option<String>, limit: Option<u64>) -> Self {
37 Self {
38 subscriber,
39 nonce,
40 limit,
41 }
42 }
43}
44
45#[derive(Debug, Deserialize, Serialize, Clone, Validate)]
47pub struct GetTriggeredTransactionsRequest {
48 #[validate(length(min = 1, message = "Subscription pubkey cannot be empty"))]
49 #[validate(custom(function = validate_pubkey))]
50 pub subscription_pubkey: String,
51
52 #[serde(skip_serializing_if = "Option::is_none")]
53 pub limit: Option<String>,
54}
55
56impl GetTriggeredTransactionsRequest {
57 pub fn new(subscription_pubkey: String, limit: Option<String>) -> Self {
58 Self {
59 subscription_pubkey,
60 limit,
61 }
62 }
63}
64
65#[derive(Debug, Deserialize, Serialize, Clone, Validate, PartialEq)]
67pub struct SendTransactionRequest {
68 #[validate(length(min = 1, message = "Transaction cannot be empty"))]
69 #[validate(custom(function = validate_transaction_data))]
70 pub transaction: String,
71
72 #[validate(nested)]
73 pub config: crate::messages::send_transaction::SendTransactionConfig,
74}
75
76impl SendTransactionRequest {
77 pub fn new(
78 transaction: String,
79 config: crate::messages::send_transaction::SendTransactionConfig,
80 ) -> Self {
81 Self {
82 transaction,
83 config,
84 }
85 }
86}
87
88#[derive(Debug, Deserialize, Serialize, Clone, Validate)]
90pub struct GetTransactionRequest {
91 #[validate(length(min = 1, message = "Signature cannot be empty"))]
92 #[validate(custom(function = validate_signature))]
93 pub signature: String,
94
95 #[serde(skip_serializing_if = "Option::is_none")]
96 pub config: Option<serde_json::Value>,
97}
98
99impl GetTransactionRequest {
100 pub fn new(signature: String, config: Option<serde_json::Value>) -> Self {
101 Self { signature, config }
102 }
103
104 pub fn new_simple(signature: String) -> Self {
105 Self {
106 signature,
107 config: Some(serde_json::json!({"encoding": "json"})),
108 }
109 }
110}
111
112#[derive(Debug, Deserialize, Serialize, Clone, Validate)]
114pub struct IsBlockhashValidRequest {
115 #[validate(length(min = 1, message = "Blockhash cannot be empty"))]
116 #[validate(custom(function = validate_base58))]
117 pub blockhash: String,
118}
119
120impl IsBlockhashValidRequest {
121 pub fn new(blockhash: String) -> Self {
122 Self { blockhash }
123 }
124
125 pub fn new_simple(blockhash: String) -> Self {
126 Self { blockhash }
127 }
128}
129
130#[derive(Debug, Deserialize, Serialize, Clone, Validate)]
132pub struct RequestAirdropRequest {
133 #[validate(length(min = 1, message = "Pubkey cannot be empty"))]
135 #[validate(custom(function = validate_pubkey))]
136 pub pubkey: String,
137
138 #[validate(custom(function = validate_airdrop_amount_i64))]
140 pub lamports: i64,
141}
142
143impl RequestAirdropRequest {
144 pub fn new(pubkey: String, lamports: i64) -> Self {
145 Self { pubkey, lamports }
146 }
147}
148
149#[derive(Debug, Clone)]
151pub enum RpcRequestParams<T> {
152 Object(T),
154 Array(Vec<serde_json::Value>),
156}
157
158impl<T> RpcRequestParams<T>
159where
160 T: for<'de> serde::Deserialize<'de>,
161{
162 pub fn from_value(value: serde_json::Value) -> Result<Self, serde_json::Error> {
164 if value.is_array() {
165 let array = value.as_array().unwrap().clone();
166 Ok(RpcRequestParams::Array(array))
167 } else {
168 let object = serde_json::from_value::<T>(value)?;
169 Ok(RpcRequestParams::Object(object))
170 }
171 }
172
173 pub fn into_object(self) -> Result<T, Box<dyn std::error::Error + Send + Sync>> {
175 match self {
176 RpcRequestParams::Object(obj) => Ok(obj),
177 RpcRequestParams::Array(_array) => {
178 Err("Array format not supported for this request type".into())
180 }
181 }
182 }
183}
184
185pub trait FromArrayParams: Sized {
187 fn from_array_params(
189 params: &[serde_json::Value],
190 ) -> Result<Self, Box<dyn std::error::Error + Send + Sync>>;
191}
192
193impl FromArrayParams for GetSubscriptionsRequest {
194 fn from_array_params(
195 params: &[serde_json::Value],
196 ) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
197 if params.is_empty() {
198 return Err("Missing subscriber parameter".into());
199 }
200
201 let subscriber = params[0]
202 .as_str()
203 .ok_or("Subscriber must be a string")?
204 .to_string();
205
206 let nonce = if let Some(nonce_param) = params.get(1) {
207 if nonce_param.is_null() {
208 None
209 } else if let Some(nonce_str) = nonce_param.as_str() {
210 Some(nonce_str.to_string())
211 } else {
212 return Err("Nonce must be a string".into());
213 }
214 } else {
215 None
216 };
217
218 let limit = params.get(2).and_then(|v| v.as_u64());
219
220 Ok(GetSubscriptionsRequest::new(subscriber, nonce, limit))
221 }
222}
223
224impl FromArrayParams for GetTriggeredTransactionsRequest {
225 fn from_array_params(
226 params: &[serde_json::Value],
227 ) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
228 if params.is_empty() {
229 return Err("Missing subscription_pubkey parameter".into());
230 }
231
232 let subscription_pubkey = params[0]
233 .as_str()
234 .ok_or("Subscription pubkey is not a string")?
235 .to_string();
236
237 let limit = params
238 .get(1)
239 .and_then(|v| v.as_str())
240 .map(|s| s.to_string());
241
242 Ok(GetTriggeredTransactionsRequest::new(
243 subscription_pubkey,
244 limit,
245 ))
246 }
247}
248
249impl FromArrayParams for SendTransactionRequest {
250 fn from_array_params(
251 params: &[serde_json::Value],
252 ) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
253 if params.len() > 2 {
254 return Err(format!(
255 "Expected one or two request params, got {}",
256 params.len()
257 ))?;
258 }
259 let transaction = params
260 .first()
261 .ok_or_else(|| "Missing the transaction parameter".to_string())?
262 .as_str()
263 .ok_or("Transaction must be a string")?
264 .to_string();
265
266 let config = match params.get(1) {
267 Some(value) => serde_json::from_value::<SendTransactionConfig>(value.clone())
268 .map_err(|e| format!("Failed to parse config: {e}"))?,
269 None => SendTransactionConfig::default(),
270 };
271 Ok(SendTransactionRequest::new(transaction, config))
272 }
273}
274
275impl FromArrayParams for GetTransactionRequest {
276 fn from_array_params(
277 params: &[serde_json::Value],
278 ) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
279 if params.is_empty() {
280 return Err("Missing signature parameter".into());
281 }
282
283 let signature = params[0]
284 .as_str()
285 .ok_or("Signature must be a string")?
286 .to_string();
287
288 let config = if params.len() > 1 {
289 Some(params[1].clone())
290 } else {
291 None
292 };
293
294 Ok(GetTransactionRequest::new(signature, config))
295 }
296}
297
298impl FromArrayParams for IsBlockhashValidRequest {
299 fn from_array_params(
300 params: &[serde_json::Value],
301 ) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
302 if params.is_empty() {
303 return Err("Missing blockhash parameter".into());
304 }
305
306 let blockhash = params[0]
307 .as_str()
308 .ok_or("Blockhash must be a string")?
309 .to_string();
310
311 Ok(IsBlockhashValidRequest::new(blockhash))
312 }
313}
314
315impl FromArrayParams for RequestAirdropRequest {
316 fn from_array_params(
317 params: &[serde_json::Value],
318 ) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
319 if params.is_empty() {
320 return Err("Missing pubkey parameter".into());
321 }
322
323 if params.len() < 2 {
324 return Err("Missing lamports parameter".into());
325 }
326
327 let pubkey = params[0]
328 .as_str()
329 .ok_or("Pubkey must be a string")?
330 .to_string();
331
332 let lamports = params[1].as_i64().ok_or("Lamports must be a number")?;
333
334 Ok(RequestAirdropRequest::new(pubkey, lamports))
335 }
336}
337
338impl FromArrayParams for GetBlockRequest {
339 fn from_array_params(
340 params: &[serde_json::Value],
341 ) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
342 if params.is_empty() {
343 return Err("Missing the block height parameter".into());
344 }
345 if params.len() > 2 {
346 return Err("Too many parameters: expected either one or two".into());
347 }
348
349 if params.len() == 1 && params[0].is_object() {
350 return Ok(serde_json::from_value(params[0].clone())?);
351 }
352
353 let block_height = params[0]
354 .as_u64()
355 .ok_or("The first parameter must be the block height")?;
356
357 let config = match params.get(1) {
358 Some(value) => Some(
359 serde_json::from_value(value.clone())
360 .map_err(|e| format!("Failed to parse the config parameter: {e}"))?,
361 ),
362 None => None,
363 };
364
365 Ok(GetBlockRequest {
366 block_height,
367 config,
368 })
369 }
370}
371
372impl FromArrayParams for GetSignatureStatusesRequest {
373 fn from_array_params(
374 params: &[serde_json::Value],
375 ) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
376 if params.is_empty() {
377 return Err("Missing the signatures parameter".into());
378 }
379 if params.len() > 2 {
380 return Err("Too many parameters: expected either one or two".into());
381 }
382
383 if params.len() == 1 && params[0].is_object() {
384 return Ok(serde_json::from_value(params[0].clone())?);
385 }
386
387 let signatures: Vec<String> = serde_json::from_value(params[0].clone())
388 .map_err(|e| format!("Failed to parse the signatures parameter: {e}"))?;
389
390 let config = match params.get(1) {
391 Some(value) => Some(
392 serde_json::from_value(value.clone())
393 .map_err(|e| format!("Failed to parse the config parameter: {e}"))?,
394 ),
395 None => None,
396 };
397
398 Ok(GetSignatureStatusesRequest { signatures, config })
399 }
400}
401
402#[cfg(test)]
403mod tests {
404 use serde_json::{from_value, json, to_value};
405
406 use super::*;
407 use crate::messages::send_transaction::{SendTransactionConfig, TransactionEncoding};
408
409 #[test]
410 fn test_get_subscriptions_request_serialization() {
411 let request = GetSubscriptionsRequest::new(
412 "84astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDVcgri".to_string(),
413 Some("test_nonce".to_string()),
414 Some(10),
415 );
416
417 let json = to_value(&request).unwrap();
418 assert_eq!(
419 json,
420 json!({
421 "subscriber": "84astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDVcgri",
422 "nonce": "test_nonce",
423 "limit": 10
424 })
425 );
426
427 let deserialized: GetSubscriptionsRequest = from_value(json).unwrap();
428 assert_eq!(deserialized.subscriber, request.subscriber);
429 assert_eq!(deserialized.nonce, request.nonce);
430 assert_eq!(deserialized.limit, request.limit);
431 }
432
433 #[test]
434 fn test_get_triggered_transactions_request_serialization() {
435 let request = GetTriggeredTransactionsRequest::new(
436 "84astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDVcgri".to_string(),
437 Some("10".to_string()),
438 );
439
440 let json = to_value(&request).unwrap();
441 assert_eq!(
442 json,
443 json!({
444 "subscription_pubkey": "84astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDVcgri",
445 "limit": "10"
446 })
447 );
448
449 let deserialized: GetTriggeredTransactionsRequest = from_value(json).unwrap();
450 assert_eq!(
451 deserialized.subscription_pubkey,
452 request.subscription_pubkey
453 );
454 assert_eq!(deserialized.limit, request.limit);
455 }
456
457 #[test]
458 fn test_get_transaction_request_serialization() {
459 let request = GetTransactionRequest::new_simple("signature123".to_string());
460
461 let json = to_value(&request).unwrap();
462 assert_eq!(
463 json,
464 json!({
465 "signature": "signature123",
466 "config": {"encoding": "json"}
467 })
468 );
469
470 let deserialized: GetTransactionRequest = from_value(json).unwrap();
471 assert_eq!(deserialized.signature, request.signature);
472 }
473
474 #[test]
475 fn test_from_array_params_get_subscriptions() {
476 let params = vec![
477 json!("84astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDVcgri"),
478 json!("test_nonce"),
479 json!(10),
480 ];
481
482 let request = GetSubscriptionsRequest::from_array_params(¶ms).unwrap();
483 assert_eq!(
484 request.subscriber,
485 "84astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDVcgri"
486 );
487 assert_eq!(request.nonce, Some("test_nonce".to_string()));
488 assert_eq!(request.limit, Some(10));
489 }
490
491 #[test]
492 fn test_from_array_params_get_subscriptions_invalid_nonce() {
493 let params = vec![
494 json!("84astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDVcgri"),
495 json!(123), ];
497
498 let result = GetSubscriptionsRequest::from_array_params(¶ms);
499 assert!(result.is_err());
500 assert_eq!(result.unwrap_err().to_string(), "Nonce must be a string");
501 }
502
503 #[test]
504 fn test_from_array_params_get_triggered_transactions() {
505 let params = vec![
506 json!("84astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDVcgri"),
507 json!("5"),
508 ];
509
510 let request = GetTriggeredTransactionsRequest::from_array_params(¶ms).unwrap();
511 assert_eq!(
512 request.subscription_pubkey,
513 "84astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDVcgri"
514 );
515 assert_eq!(request.limit, Some("5".to_string()));
516 }
517
518 #[test]
519 fn test_from_array_params_get_transaction() {
520 let params = vec![json!("signature123"), json!({"encoding": "json"})];
521
522 let request = GetTransactionRequest::from_array_params(¶ms).unwrap();
523 assert_eq!(request.signature, "signature123");
524 assert!(request.config.is_some());
525 }
526
527 #[test]
528 fn test_from_array_params_is_blockhash_valid() {
529 let params = vec![json!("blockhash123")];
530
531 let request = IsBlockhashValidRequest::from_array_params(¶ms).unwrap();
532 assert_eq!(request.blockhash, "blockhash123");
533 }
534
535 #[test]
536 fn test_from_array_params_request_airdrop() {
537 let params = vec![
538 json!("83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcgri"),
539 json!(1000000),
540 ];
541
542 let request = RequestAirdropRequest::from_array_params(¶ms).unwrap();
543 assert_eq!(
544 request.pubkey,
545 "83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcgri"
546 );
547 assert_eq!(request.lamports, 1000000);
548 }
549
550 #[test]
551 fn test_from_array_params_request_airdrop_missing_params() {
552 let params = vec![json!("83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcgri")];
553
554 let result = RequestAirdropRequest::from_array_params(¶ms);
555 assert!(result.is_err());
556 assert_eq!(
557 result.unwrap_err().to_string(),
558 "Missing lamports parameter"
559 );
560 }
561
562 #[test]
563 fn test_from_array_params_request_airdrop_invalid_type() {
564 let params = vec![
565 json!("83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcgri"),
566 json!("thousand"), ];
568
569 let result = RequestAirdropRequest::from_array_params(¶ms);
570 assert!(result.is_err());
571 assert_eq!(result.unwrap_err().to_string(), "Lamports must be a number");
572 }
573
574 #[test]
575 fn test_send_transaction_from_array_missing_optional_config() {
576 let encoded_tx = "4hXTCkRzt9WyecNzV1XPgCDfGAZzQKNxLXgynz5QDuWWPSAZBZSHptvWRL3BjCvzUXRdKvHL2b7yGrRQcWyaqsaBCncVG7BFggS8w9snUts67BSh3EqKpXLUm5UMHfD7ZBe9GhARjbNQMLJ1QD3Spr6oMTBU6EhdB4RD8CP2xUxr2u3d6fos36PD98XS6oX8TQjLpsMwncs5DAMiD4nNnR8NBfyghGCWvCVifVwvA8B8TJxE1aiyiv2L429BCWfyzAme5sZW8rDb14NeCQHhZbtNqfXhcp2tAnaAT";
577 let params = vec![json!(encoded_tx)];
578 assert_eq!(
579 SendTransactionRequest::from_array_params(¶ms)
580 .expect("failed to parse a transaction from params"),
581 SendTransactionRequest {
582 transaction: encoded_tx.to_string(),
583 config: SendTransactionConfig::default()
584 }
585 );
586 }
587
588 #[test]
589 fn test_send_transaction_from_array_default_config() {
590 let encoded_tx = "4hXTCkRzt9WyecNzV1XPgCDfGAZzQKNxLXgynz5QDuWWPSAZBZSHptvWRL3BjCvzUXRdKvHL2b7yGrRQcWyaqsaBCncVG7BFggS8w9snUts67BSh3EqKpXLUm5UMHfD7ZBe9GhARjbNQMLJ1QD3Spr6oMTBU6EhdB4RD8CP2xUxr2u3d6fos36PD98XS6oX8TQjLpsMwncs5DAMiD4nNnR8NBfyghGCWvCVifVwvA8B8TJxE1aiyiv2L429BCWfyzAme5sZW8rDb14NeCQHhZbtNqfXhcp2tAnaAT";
591 let params = vec![json!(encoded_tx), json!({})];
592 assert_eq!(
593 SendTransactionRequest::from_array_params(¶ms)
594 .expect("failed to parse a transaction from params"),
595 SendTransactionRequest {
596 transaction: encoded_tx.to_string(),
597 config: SendTransactionConfig::default(),
598 }
599 );
600 }
601
602 #[test]
603 fn test_send_transaction_from_array_config() {
604 let encoded_tx = "4hXTCkRzt9WyecNzV1XPgCDfGAZzQKNxLXgynz5QDuWWPSAZBZSHptvWRL3BjCvzUXRdKvHL2b7yGrRQcWyaqsaBCncVG7BFggS8w9snUts67BSh3EqKpXLUm5UMHfD7ZBe9GhARjbNQMLJ1QD3Spr6oMTBU6EhdB4RD8CP2xUxr2u3d6fos36PD98XS6oX8TQjLpsMwncs5DAMiD4nNnR8NBfyghGCWvCVifVwvA8B8TJxE1aiyiv2L429BCWfyzAme5sZW8rDb14NeCQHhZbtNqfXhcp2tAnaAT";
605 let params = vec![
606 json!(encoded_tx),
607 json!({
608 "encoding": "base58",
609 }),
610 ];
611 assert_eq!(
612 SendTransactionRequest::from_array_params(¶ms)
613 .expect("failed to parse a transaction from params"),
614 SendTransactionRequest {
615 transaction: encoded_tx.to_string(),
616 config: SendTransactionConfig::new(TransactionEncoding::Base58),
617 }
618 );
619 }
620
621 #[test]
622 fn test_send_transaction_from_array_too_many_params() {
623 let encoded_tx = "4hXTCkRzt9WyecNzV1XPgCDfGAZzQKNxLXgynz5QDuWWPSAZBZSHptvWRL3BjCvzUXRdKvHL2b7yGrRQcWyaqsaBCncVG7BFggS8w9snUts67BSh3EqKpXLUm5UMHfD7ZBe9GhARjbNQMLJ1QD3Spr6oMTBU6EhdB4RD8CP2xUxr2u3d6fos36PD98XS6oX8TQjLpsMwncs5DAMiD4nNnR8NBfyghGCWvCVifVwvA8B8TJxE1aiyiv2L429BCWfyzAme5sZW8rDb14NeCQHhZbtNqfXhcp2tAnaAT";
624 let params = vec![json!(encoded_tx), json!({}), json!({})];
625 SendTransactionRequest::from_array_params(¶ms).unwrap_err();
626 }
627
628 #[test]
629 fn test_from_array_params_get_block_basic() {
630 let params = vec![json!(123456)];
631
632 let request = GetBlockRequest::from_array_params(¶ms).unwrap();
633 assert_eq!(request.block_height, 123456);
634 assert!(request.config.is_none());
635 }
636
637 #[test]
638 fn test_from_array_params_get_block_with_config() {
639 let params = vec![
640 json!(123456),
641 json!({
642 "transactionDetails": "full"
643 }),
644 ];
645
646 let request = GetBlockRequest::from_array_params(¶ms).unwrap();
647 assert_eq!(request.block_height, 123456);
648 assert!(request.config.is_some());
649 }
650
651 #[test]
652 fn test_from_array_params_get_block_single_object() {
653 let params = vec![json!({
654 "blockHeight": 789012,
655 "config": {
656 "transactionDetails": "signatures"
657 }
658 })];
659
660 let request = GetBlockRequest::from_array_params(¶ms).unwrap();
661 assert_eq!(request.block_height, 789012);
662 assert!(request.config.is_some());
663 }
664
665 #[test]
666 #[should_panic(expected = "block height")]
667 fn test_from_array_params_get_block_empty_params() {
668 GetBlockRequest::from_array_params(&[]).unwrap();
669 }
670
671 #[test]
672 #[should_panic(expected = "Too many parameters")]
673 fn test_from_array_params_get_block_too_many_params() {
674 GetBlockRequest::from_array_params(&[json!(123456), json!({}), json!({})]).unwrap();
675 }
676
677 #[test]
678 #[should_panic(expected = "block height")]
679 fn test_from_array_params_get_block_invalid_height_type() {
680 GetBlockRequest::from_array_params(&[json!("not_a_number")]).unwrap();
681 }
682
683 #[test]
684 #[should_panic(expected = "config parameter")]
685 fn test_from_array_params_get_block_invalid_config() {
686 GetBlockRequest::from_array_params(&[
687 json!(123456),
688 json!("invalid_config"), ])
690 .unwrap();
691 }
692
693 #[test]
694 #[should_panic(expected = "config parameter")]
695 fn test_from_array_params_get_block_null_config() {
696 GetBlockRequest::from_array_params(&[json!(123456), json!(null)]).unwrap();
697 }
698
699 #[test]
700 fn test_from_array_params_get_signature_statuses_basic() {
701 let signatures = vec![
702 "5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW",
703 "4hXTCkRzt9WyecNzV1XPgCDfGAZzQKNxLXgynz5QDuWWPSAZBZSHptvWRL3BjCvzUXRdKvHL2b7yGrRQcWyaqsaB"
704 ];
705 let params = vec![json!(signatures)];
706
707 let request = GetSignatureStatusesRequest::from_array_params(¶ms).unwrap();
708 assert_eq!(request.signatures, signatures);
709 assert!(request.config.is_none());
710 }
711
712 #[test]
713 fn test_from_array_params_get_signature_statuses_with_config() {
714 let signatures = vec![
715 "5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW"
716 ];
717 let config = json!({"searchTransactionHistory": true});
718 let params = vec![json!(signatures), config.clone()];
719
720 let request = GetSignatureStatusesRequest::from_array_params(¶ms).unwrap();
721 assert_eq!(request.signatures, signatures);
722 assert!(request.config.is_some());
723 }
724
725 #[test]
726 fn test_from_array_params_get_signature_statuses_single_object() {
727 let signatures = vec![
728 "5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW"
729 ];
730 let params = vec![json!({
731 "signatures": signatures,
732 "config": {"searchTransactionHistory": false}
733 })];
734
735 let request = GetSignatureStatusesRequest::from_array_params(¶ms).unwrap();
736 assert_eq!(request.signatures, signatures);
737 assert!(request.config.is_some());
738 }
739
740 #[test]
741 fn test_from_array_params_get_signature_statuses_empty_signatures() {
742 let request = GetSignatureStatusesRequest::from_array_params(&[json!([])]).unwrap();
743 assert!(request.signatures.is_empty());
744 assert!(request.config.is_none());
745 }
746
747 #[test]
748 #[should_panic(expected = "Missing the signatures parameter")]
749 fn test_from_array_params_get_signature_statuses_empty_params() {
750 GetSignatureStatusesRequest::from_array_params(&[]).unwrap();
751 }
752
753 #[test]
754 #[should_panic(expected = "Too many parameters")]
755 fn test_from_array_params_get_signature_statuses_too_many_params() {
756 GetSignatureStatusesRequest::from_array_params(&[json!(["sig1"]), json!({}), json!({})])
757 .unwrap();
758 }
759
760 #[test]
761 #[should_panic(expected = "Failed to parse the signatures parameter")]
762 fn test_from_array_params_get_signature_statuses_invalid_signatures_type() {
763 GetSignatureStatusesRequest::from_array_params(&[json!("not an array")]).unwrap();
764 }
765
766 #[test]
767 #[should_panic(expected = "Failed to parse the config parameter")]
768 fn test_from_array_params_get_signature_statuses_invalid_config() {
769 GetSignatureStatusesRequest::from_array_params(&[
770 json!(["5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW"]),
771 json!("invalid config")
772 ]).unwrap();
773 }
774
775 #[test]
776 fn test_from_array_params_get_signature_statuses_multiple_signatures() {
777 let signatures: Vec<&str> = (0..100).map(|i| {
778 if i % 2 == 0 {
779 "5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW"
780 } else {
781 "4hXTCkRzt9WyecNzV1XPgCDfGAZzQKNxLXgynz5QDuWWPSAZBZSHptvWRL3BjCvzUXRdKvHL2b7yGrRQcWyaqsaB"
782 }
783 }).collect();
784 let params = vec![json!(signatures)];
785
786 let request = GetSignatureStatusesRequest::from_array_params(¶ms).unwrap();
787 assert_eq!(request.signatures.len(), 100);
788 assert!(request.config.is_none());
789 }
790
791 #[test]
792 #[should_panic(expected = "Failed to parse the config parameter")]
793 fn test_from_array_params_get_signature_statuses_null_config() {
794 GetSignatureStatusesRequest::from_array_params(&[
795 json!(["5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW"]),
796 json!(null)]
797 ).unwrap();
798 }
799}