use serde::{Deserialize, Serialize};
use validator::Validate;
use crate::{
messages::send_transaction::SendTransactionConfig,
validation::{
validate_airdrop_amount_i64, validate_base58, validate_protocol_version, validate_pubkey,
validate_signature, validate_transaction_data,
},
GetBlockRequest, GetSignatureStatusesRequest,
};
#[derive(Debug, Deserialize, Serialize, Clone, Validate)]
pub struct GetSubscriptionRequest {
#[serde(default)]
#[validate(custom(function = validate_protocol_version))]
pub version: u16,
#[validate(length(min = 1, message = "Subscriber pubkey cannot be empty"))]
#[validate(custom(function = validate_pubkey))]
pub subscriber: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub nonce: Option<String>,
}
impl GetSubscriptionRequest {
pub fn new(subscriber: String, nonce: Option<String>) -> Self {
Self {
version: 0,
subscriber,
nonce,
}
}
}
#[derive(Debug, Deserialize, Serialize, Clone, Validate)]
pub struct GetTriggeredTransactionsRequest {
#[serde(default)]
#[validate(custom(function = validate_protocol_version))]
pub version: u16,
#[validate(length(min = 1, message = "Subscription pubkey cannot be empty"))]
#[validate(custom(function = validate_pubkey))]
pub subscription_pubkey: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub limit: Option<String>,
}
impl GetTriggeredTransactionsRequest {
pub fn new(subscription_pubkey: String, limit: Option<String>) -> Self {
Self {
version: 0,
subscription_pubkey,
limit,
}
}
}
#[derive(Debug, Deserialize, Serialize, Clone, Validate, PartialEq)]
pub struct SendTransactionRequest {
#[validate(length(min = 1, message = "Transaction cannot be empty"))]
#[validate(custom(function = validate_transaction_data))]
pub transaction: String,
#[validate(nested)]
pub config: crate::messages::send_transaction::SendTransactionConfig,
}
impl SendTransactionRequest {
pub fn new(
transaction: String,
config: crate::messages::send_transaction::SendTransactionConfig,
) -> Self {
Self {
transaction,
config,
}
}
}
#[derive(Debug, Deserialize, Serialize, Clone, Validate)]
pub struct GetTransactionRequest {
#[validate(length(min = 1, message = "Signature cannot be empty"))]
#[validate(custom(function = validate_signature))]
pub signature: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub config: Option<serde_json::Value>,
}
impl GetTransactionRequest {
pub fn new(signature: String, config: Option<serde_json::Value>) -> Self {
Self { signature, config }
}
pub fn new_simple(signature: String) -> Self {
Self {
signature,
config: Some(serde_json::json!({"encoding": "json"})),
}
}
}
#[derive(Debug, Deserialize, Serialize, Clone, Validate)]
pub struct IsBlockhashValidRequest {
#[serde(default)]
#[validate(custom(function = validate_protocol_version))]
pub version: u16,
#[validate(length(min = 1, message = "Blockhash cannot be empty"))]
#[validate(custom(function = validate_base58))]
pub blockhash: String,
}
impl IsBlockhashValidRequest {
pub fn new(blockhash: String) -> Self {
Self {
version: 0,
blockhash,
}
}
pub fn new_simple(blockhash: String) -> Self {
Self {
version: 0,
blockhash,
}
}
}
#[derive(Debug, Deserialize, Serialize, Clone, Validate)]
pub struct RequestAirdropRequest {
#[validate(length(min = 1, message = "Pubkey cannot be empty"))]
#[validate(custom(function = validate_pubkey))]
pub pubkey: String,
#[validate(custom(function = validate_airdrop_amount_i64))]
pub kelvins: i64,
}
impl RequestAirdropRequest {
pub fn new(pubkey: String, kelvins: i64) -> Self {
Self { pubkey, kelvins }
}
}
#[derive(Debug, Clone)]
pub enum RpcRequestParams<T> {
Object(T),
Array(Vec<serde_json::Value>),
}
impl<T> RpcRequestParams<T>
where
T: for<'de> serde::Deserialize<'de>,
{
pub fn from_value(value: serde_json::Value) -> Result<Self, serde_json::Error> {
if value.is_array() {
let array = value.as_array().unwrap().clone();
Ok(RpcRequestParams::Array(array))
} else {
let object = serde_json::from_value::<T>(value)?;
Ok(RpcRequestParams::Object(object))
}
}
pub fn into_object(self) -> Result<T, Box<dyn std::error::Error + Send + Sync>> {
match self {
RpcRequestParams::Object(obj) => Ok(obj),
RpcRequestParams::Array(_array) => {
Err("Array format not supported for this request type".into())
}
}
}
}
pub trait FromArrayParams: Sized {
fn from_array_params(
params: &[serde_json::Value],
) -> Result<Self, Box<dyn std::error::Error + Send + Sync>>;
}
impl FromArrayParams for GetSubscriptionRequest {
fn from_array_params(
params: &[serde_json::Value],
) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
if params.is_empty() {
return Err("Missing subscriber parameter".into());
}
let subscriber = params[0]
.as_str()
.ok_or("Subscriber must be a string")?
.to_string();
let nonce = if let Some(nonce_param) = params.get(1) {
if nonce_param.is_null() {
None
} else if let Some(nonce_str) = nonce_param.as_str() {
Some(nonce_str.to_string())
} else {
return Err("Nonce must be a string".into());
}
} else {
None
};
Ok(GetSubscriptionRequest::new(subscriber, nonce))
}
}
impl FromArrayParams for GetTriggeredTransactionsRequest {
fn from_array_params(
params: &[serde_json::Value],
) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
if params.is_empty() {
return Err("Missing subscription_pubkey parameter".into());
}
let subscription_pubkey = params[0]
.as_str()
.ok_or("Subscription pubkey is not a string")?
.to_string();
let limit = params
.get(1)
.and_then(|v| v.as_str())
.map(|s| s.to_string());
Ok(GetTriggeredTransactionsRequest::new(
subscription_pubkey,
limit,
))
}
}
impl FromArrayParams for SendTransactionRequest {
fn from_array_params(
params: &[serde_json::Value],
) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
if params.len() > 2 {
return Err(format!(
"Expected one or two request params, got {}",
params.len()
))?;
}
let transaction = params
.first()
.ok_or_else(|| "Missing the transaction parameter".to_string())?
.as_str()
.ok_or("Transaction must be a string")?
.to_string();
let config = match params.get(1) {
Some(value) => serde_json::from_value::<SendTransactionConfig>(value.clone())
.map_err(|e| format!("Failed to parse config: {e}"))?,
None => SendTransactionConfig::default(),
};
Ok(SendTransactionRequest::new(transaction, config))
}
}
impl FromArrayParams for GetTransactionRequest {
fn from_array_params(
params: &[serde_json::Value],
) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
if params.is_empty() {
return Err("Missing signature parameter".into());
}
let signature = params[0]
.as_str()
.ok_or("Signature must be a string")?
.to_string();
let config = if params.len() > 1 {
Some(params[1].clone())
} else {
None
};
Ok(GetTransactionRequest::new(signature, config))
}
}
impl FromArrayParams for IsBlockhashValidRequest {
fn from_array_params(
params: &[serde_json::Value],
) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
if params.is_empty() {
return Err("Missing blockhash parameter".into());
}
let blockhash = params[0]
.as_str()
.ok_or("Blockhash must be a string")?
.to_string();
Ok(IsBlockhashValidRequest::new(blockhash))
}
}
impl FromArrayParams for RequestAirdropRequest {
fn from_array_params(
params: &[serde_json::Value],
) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
if params.is_empty() {
return Err("Missing pubkey parameter".into());
}
if params.len() < 2 {
return Err("Missing kelvins parameter".into());
}
let pubkey = params[0]
.as_str()
.ok_or("Pubkey must be a string")?
.to_string();
let kelvins = params[1].as_i64().ok_or("Kelvins must be a number")?;
Ok(RequestAirdropRequest::new(pubkey, kelvins))
}
}
impl FromArrayParams for GetBlockRequest {
fn from_array_params(
params: &[serde_json::Value],
) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
if params.is_empty() {
return Err("Missing the block height parameter".into());
}
if params.len() > 2 {
return Err("Too many parameters: expected either one or two".into());
}
if params.len() == 1 && params[0].is_object() {
return Ok(serde_json::from_value(params[0].clone())?);
}
let block_height = params[0]
.as_u64()
.ok_or("The first parameter must be the block height")?;
let config = match params.get(1) {
Some(value) => Some(
serde_json::from_value(value.clone())
.map_err(|e| format!("Failed to parse the config parameter: {e}"))?,
),
None => None,
};
Ok(GetBlockRequest {
version: 0,
block_height,
config,
})
}
}
impl FromArrayParams for GetSignatureStatusesRequest {
fn from_array_params(
params: &[serde_json::Value],
) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
if params.is_empty() {
return Err("Missing the signatures parameter".into());
}
if params.len() > 2 {
return Err("Too many parameters: expected either one or two".into());
}
if params.len() == 1 && params[0].is_object() {
return Ok(serde_json::from_value(params[0].clone())?);
}
let signatures: Vec<String> = serde_json::from_value(params[0].clone())
.map_err(|e| format!("Failed to parse the signatures parameter: {e}"))?;
let config = match params.get(1) {
Some(value) => Some(
serde_json::from_value(value.clone())
.map_err(|e| format!("Failed to parse the config parameter: {e}"))?,
),
None => None,
};
Ok(GetSignatureStatusesRequest {
version: 0,
signatures,
config,
})
}
}
#[cfg(test)]
mod tests {
use serde_json::{from_value, json, to_value};
use super::*;
use crate::messages::send_transaction::{SendTransactionConfig, TransactionEncoding};
#[test]
fn test_get_subscription_request_serialization() {
let request = GetSubscriptionRequest::new(
"84astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDVcgri".to_string(),
Some("test_nonce".to_string()),
);
let json = to_value(&request).unwrap();
assert_eq!(
json,
json!({
"version": 0,
"subscriber": "84astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDVcgri",
"nonce": "test_nonce"
})
);
let deserialized: GetSubscriptionRequest = from_value(json).unwrap();
assert_eq!(deserialized.version, 0);
assert_eq!(deserialized.subscriber, request.subscriber);
assert_eq!(deserialized.nonce, request.nonce);
}
#[test]
fn test_get_triggered_transactions_request_serialization() {
let request = GetTriggeredTransactionsRequest::new(
"84astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDVcgri".to_string(),
Some("10".to_string()),
);
let json = to_value(&request).unwrap();
assert_eq!(
json,
json!({
"version": 0,
"subscription_pubkey": "84astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDVcgri",
"limit": "10"
})
);
let deserialized: GetTriggeredTransactionsRequest = from_value(json).unwrap();
assert_eq!(deserialized.version, 0);
assert_eq!(
deserialized.subscription_pubkey,
request.subscription_pubkey
);
assert_eq!(deserialized.limit, request.limit);
}
#[test]
fn test_get_transaction_request_serialization() {
let request = GetTransactionRequest::new_simple("signature123".to_string());
let json = to_value(&request).unwrap();
assert_eq!(
json,
json!({
"signature": "signature123",
"config": {"encoding": "json"}
})
);
let deserialized: GetTransactionRequest = from_value(json).unwrap();
assert_eq!(deserialized.signature, request.signature);
}
#[test]
fn test_from_array_params_get_subscription() {
let params = vec![
json!("84astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDVcgri"),
json!("test_nonce"),
];
let request = GetSubscriptionRequest::from_array_params(¶ms).unwrap();
assert_eq!(
request.subscriber,
"84astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDVcgri"
);
assert_eq!(request.nonce, Some("test_nonce".to_string()));
}
#[test]
fn test_from_array_params_get_subscription_invalid_nonce() {
let params = vec![
json!("84astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDVcgri"),
json!(123), ];
let result = GetSubscriptionRequest::from_array_params(¶ms);
assert!(result.is_err());
assert_eq!(result.unwrap_err().to_string(), "Nonce must be a string");
}
#[test]
fn test_from_array_params_get_triggered_transactions() {
let params = vec![
json!("84astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDVcgri"),
json!("5"),
];
let request = GetTriggeredTransactionsRequest::from_array_params(¶ms).unwrap();
assert_eq!(
request.subscription_pubkey,
"84astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDVcgri"
);
assert_eq!(request.limit, Some("5".to_string()));
}
#[test]
fn test_from_array_params_get_transaction() {
let params = vec![json!("signature123"), json!({"encoding": "json"})];
let request = GetTransactionRequest::from_array_params(¶ms).unwrap();
assert_eq!(request.signature, "signature123");
assert!(request.config.is_some());
}
#[test]
fn test_from_array_params_is_blockhash_valid() {
let params = vec![json!("blockhash123")];
let request = IsBlockhashValidRequest::from_array_params(¶ms).unwrap();
assert_eq!(request.blockhash, "blockhash123");
}
#[test]
fn test_from_array_params_request_airdrop() {
let params = vec![
json!("83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcgri"),
json!(1000000),
];
let request = RequestAirdropRequest::from_array_params(¶ms).unwrap();
assert_eq!(
request.pubkey,
"83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcgri"
);
assert_eq!(request.kelvins, 1000000);
}
#[test]
fn test_from_array_params_request_airdrop_missing_params() {
let params = vec![json!("83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcgri")];
let result = RequestAirdropRequest::from_array_params(¶ms);
assert!(result.is_err());
assert_eq!(result.unwrap_err().to_string(), "Missing kelvins parameter");
}
#[test]
fn test_from_array_params_request_airdrop_invalid_type() {
let params = vec![
json!("83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcgri"),
json!("thousand"), ];
let result = RequestAirdropRequest::from_array_params(¶ms);
assert!(result.is_err());
assert_eq!(result.unwrap_err().to_string(), "Kelvins must be a number");
}
#[test]
fn test_send_transaction_from_array_missing_optional_config() {
let encoded_tx = "4hXTCkRzt9WyecNzV1XPgCDfGAZzQKNxLXgynz5QDuWWPSAZBZSHptvWRL3BjCvzUXRdKvHL2b7yGrRQcWyaqsaBCncVG7BFggS8w9snUts67BSh3EqKpXLUm5UMHfD7ZBe9GhARjbNQMLJ1QD3Spr6oMTBU6EhdB4RD8CP2xUxr2u3d6fos36PD98XS6oX8TQjLpsMwncs5DAMiD4nNnR8NBfyghGCWvCVifVwvA8B8TJxE1aiyiv2L429BCWfyzAme5sZW8rDb14NeCQHhZbtNqfXhcp2tAnaAT";
let params = vec![json!(encoded_tx)];
assert_eq!(
SendTransactionRequest::from_array_params(¶ms)
.expect("failed to parse a transaction from params"),
SendTransactionRequest {
transaction: encoded_tx.to_string(),
config: SendTransactionConfig::default()
}
);
}
#[test]
fn test_send_transaction_from_array_default_config() {
let encoded_tx = "4hXTCkRzt9WyecNzV1XPgCDfGAZzQKNxLXgynz5QDuWWPSAZBZSHptvWRL3BjCvzUXRdKvHL2b7yGrRQcWyaqsaBCncVG7BFggS8w9snUts67BSh3EqKpXLUm5UMHfD7ZBe9GhARjbNQMLJ1QD3Spr6oMTBU6EhdB4RD8CP2xUxr2u3d6fos36PD98XS6oX8TQjLpsMwncs5DAMiD4nNnR8NBfyghGCWvCVifVwvA8B8TJxE1aiyiv2L429BCWfyzAme5sZW8rDb14NeCQHhZbtNqfXhcp2tAnaAT";
let params = vec![json!(encoded_tx), json!({})];
assert_eq!(
SendTransactionRequest::from_array_params(¶ms)
.expect("failed to parse a transaction from params"),
SendTransactionRequest {
transaction: encoded_tx.to_string(),
config: SendTransactionConfig::default(),
}
);
}
#[test]
fn test_send_transaction_from_array_config() {
let encoded_tx = "4hXTCkRzt9WyecNzV1XPgCDfGAZzQKNxLXgynz5QDuWWPSAZBZSHptvWRL3BjCvzUXRdKvHL2b7yGrRQcWyaqsaBCncVG7BFggS8w9snUts67BSh3EqKpXLUm5UMHfD7ZBe9GhARjbNQMLJ1QD3Spr6oMTBU6EhdB4RD8CP2xUxr2u3d6fos36PD98XS6oX8TQjLpsMwncs5DAMiD4nNnR8NBfyghGCWvCVifVwvA8B8TJxE1aiyiv2L429BCWfyzAme5sZW8rDb14NeCQHhZbtNqfXhcp2tAnaAT";
let params = vec![
json!(encoded_tx),
json!({
"encoding": "base58",
}),
];
assert_eq!(
SendTransactionRequest::from_array_params(¶ms)
.expect("failed to parse a transaction from params"),
SendTransactionRequest {
transaction: encoded_tx.to_string(),
config: SendTransactionConfig::new(TransactionEncoding::Base58),
}
);
}
#[test]
fn test_send_transaction_from_array_too_many_params() {
let encoded_tx = "4hXTCkRzt9WyecNzV1XPgCDfGAZzQKNxLXgynz5QDuWWPSAZBZSHptvWRL3BjCvzUXRdKvHL2b7yGrRQcWyaqsaBCncVG7BFggS8w9snUts67BSh3EqKpXLUm5UMHfD7ZBe9GhARjbNQMLJ1QD3Spr6oMTBU6EhdB4RD8CP2xUxr2u3d6fos36PD98XS6oX8TQjLpsMwncs5DAMiD4nNnR8NBfyghGCWvCVifVwvA8B8TJxE1aiyiv2L429BCWfyzAme5sZW8rDb14NeCQHhZbtNqfXhcp2tAnaAT";
let params = vec![json!(encoded_tx), json!({}), json!({})];
SendTransactionRequest::from_array_params(¶ms).unwrap_err();
}
#[test]
fn test_from_array_params_get_block_basic() {
let params = vec![json!(123456)];
let request = GetBlockRequest::from_array_params(¶ms).unwrap();
assert_eq!(request.block_height, 123456);
assert!(request.config.is_none());
}
#[test]
fn test_from_array_params_get_block_with_config() {
let params = vec![
json!(123456),
json!({
"transactionDetails": "full"
}),
];
let request = GetBlockRequest::from_array_params(¶ms).unwrap();
assert_eq!(request.block_height, 123456);
assert!(request.config.is_some());
}
#[test]
fn test_from_array_params_get_block_single_object() {
let params = vec![json!({
"version": 0,
"blockHeight": 789012,
"config": {
"transactionDetails": "signatures"
}
})];
let request = GetBlockRequest::from_array_params(¶ms).unwrap();
assert_eq!(request.block_height, 789012);
assert!(request.config.is_some());
}
#[test]
#[should_panic(expected = "block height")]
fn test_from_array_params_get_block_empty_params() {
GetBlockRequest::from_array_params(&[]).unwrap();
}
#[test]
#[should_panic(expected = "Too many parameters")]
fn test_from_array_params_get_block_too_many_params() {
GetBlockRequest::from_array_params(&[json!(123456), json!({}), json!({})]).unwrap();
}
#[test]
#[should_panic(expected = "block height")]
fn test_from_array_params_get_block_invalid_height_type() {
GetBlockRequest::from_array_params(&[json!("not_a_number")]).unwrap();
}
#[test]
#[should_panic(expected = "config parameter")]
fn test_from_array_params_get_block_invalid_config() {
GetBlockRequest::from_array_params(&[
json!(123456),
json!("invalid_config"), ])
.unwrap();
}
#[test]
#[should_panic(expected = "config parameter")]
fn test_from_array_params_get_block_null_config() {
GetBlockRequest::from_array_params(&[json!(123456), json!(null)]).unwrap();
}
#[test]
fn test_from_array_params_get_signature_statuses_basic() {
let signatures = vec![
"5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW",
"4hXTCkRzt9WyecNzV1XPgCDfGAZzQKNxLXgynz5QDuWWPSAZBZSHptvWRL3BjCvzUXRdKvHL2b7yGrRQcWyaqsaB"
];
let params = vec![json!(signatures)];
let request = GetSignatureStatusesRequest::from_array_params(¶ms).unwrap();
assert_eq!(request.signatures, signatures);
assert!(request.config.is_none());
}
#[test]
fn test_from_array_params_get_signature_statuses_with_config() {
let signatures = vec![
"5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW"
];
let config = json!({"searchTransactionHistory": true});
let params = vec![json!(signatures), config.clone()];
let request = GetSignatureStatusesRequest::from_array_params(¶ms).unwrap();
assert_eq!(request.signatures, signatures);
assert!(request.config.is_some());
}
#[test]
fn test_from_array_params_get_signature_statuses_single_object() {
let signatures = vec![
"5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW"
];
let params = vec![json!({
"version": 0,
"signatures": signatures,
"config": {"searchTransactionHistory": false}
})];
let request = GetSignatureStatusesRequest::from_array_params(¶ms).unwrap();
assert_eq!(request.signatures, signatures);
assert!(request.config.is_some());
}
#[test]
fn test_from_array_params_get_signature_statuses_empty_signatures() {
let request = GetSignatureStatusesRequest::from_array_params(&[json!([])]).unwrap();
assert!(request.signatures.is_empty());
assert!(request.config.is_none());
}
#[test]
#[should_panic(expected = "Missing the signatures parameter")]
fn test_from_array_params_get_signature_statuses_empty_params() {
GetSignatureStatusesRequest::from_array_params(&[]).unwrap();
}
#[test]
#[should_panic(expected = "Too many parameters")]
fn test_from_array_params_get_signature_statuses_too_many_params() {
GetSignatureStatusesRequest::from_array_params(&[json!(["sig1"]), json!({}), json!({})])
.unwrap();
}
#[test]
#[should_panic(expected = "Failed to parse the signatures parameter")]
fn test_from_array_params_get_signature_statuses_invalid_signatures_type() {
GetSignatureStatusesRequest::from_array_params(&[json!("not an array")]).unwrap();
}
#[test]
#[should_panic(expected = "Failed to parse the config parameter")]
fn test_from_array_params_get_signature_statuses_invalid_config() {
GetSignatureStatusesRequest::from_array_params(&[
json!(["5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW"]),
json!("invalid config")
]).unwrap();
}
#[test]
fn test_from_array_params_get_signature_statuses_multiple_signatures() {
let signatures: Vec<&str> = (0..100).map(|i| {
if i % 2 == 0 {
"5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW"
} else {
"4hXTCkRzt9WyecNzV1XPgCDfGAZzQKNxLXgynz5QDuWWPSAZBZSHptvWRL3BjCvzUXRdKvHL2b7yGrRQcWyaqsaB"
}
}).collect();
let params = vec![json!(signatures)];
let request = GetSignatureStatusesRequest::from_array_params(¶ms).unwrap();
assert_eq!(request.signatures.len(), 100);
assert!(request.config.is_none());
}
#[test]
#[should_panic(expected = "Failed to parse the config parameter")]
fn test_from_array_params_get_signature_statuses_null_config() {
GetSignatureStatusesRequest::from_array_params(&[
json!(["5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW"]),
json!(null)]
).unwrap();
}
}