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