diff --git a/packages/puppeteer-base/src/lib.rs b/packages/puppeteer-base/src/lib.rs index 2be90df0..6960247e 100644 --- a/packages/puppeteer-base/src/lib.rs +++ b/packages/puppeteer-base/src/lib.rs @@ -6,3 +6,5 @@ pub mod query; pub mod reply; pub mod state; pub mod sudo; +#[cfg(test)] +mod tests; diff --git a/packages/puppeteer-base/src/sudo.rs b/packages/puppeteer-base/src/sudo.rs index f1ec815e..af72fcaf 100644 --- a/packages/puppeteer-base/src/sudo.rs +++ b/packages/puppeteer-base/src/sudo.rs @@ -179,6 +179,11 @@ impl KVReconstruct for BalancesAndDelegations { fn reconstruct( storage_values: &[neutron_sdk::bindings::types::StorageValue], ) -> NeutronResult { + if storage_values.is_empty() { + return Err(NeutronError::InvalidQueryResultFormat( + "storage_values length is 0".into(), + )); + } let mut coins: Vec = Vec::with_capacity(storage_values.len()); let kv = &storage_values[0]; if kv.value.len() > 0 { @@ -189,11 +194,6 @@ impl KVReconstruct for BalancesAndDelegations { let mut delegations: Vec = Vec::with_capacity((storage_values.len() - 2) / 2); - if storage_values.is_empty() { - return Err(NeutronError::InvalidQueryResultFormat( - "storage_values length is 0".into(), - )); - } // first StorageValue is denom if storage_values[1].value.is_empty() { // Incoming denom cannot be empty, it should always be configured on chain. diff --git a/packages/puppeteer-base/src/tests.rs b/packages/puppeteer-base/src/tests.rs new file mode 100644 index 00000000..9160177e --- /dev/null +++ b/packages/puppeteer-base/src/tests.rs @@ -0,0 +1,132 @@ +use super::sudo::BalancesAndDelegations; +use cosmwasm_std::{to_json_binary, Addr, Binary, Coin, Delegation, Uint128}; +use neutron_sdk::bindings::types::StorageValue; +use neutron_sdk::interchain_queries::types::KVReconstruct; +use neutron_sdk::NeutronResult; +use prost::Message; + +#[test] +fn test_reconstruct_balance_and_delegations_no_delegations() { + let coin = cosmos_sdk_proto::cosmos::base::v1beta1::Coin { + denom: "uatom".to_string(), + amount: "1000".to_string(), + }; + let mut buf_coin = Vec::new(); + coin.encode(&mut buf_coin).unwrap(); + let storage_values: Vec = vec![ + StorageValue { + storage_prefix: "prefix".to_string(), + key: Binary::from("balances".as_bytes()), + value: buf_coin.into(), + }, + StorageValue { + storage_prefix: "prefix".to_string(), + key: Binary::from("denom".as_bytes()), + value: to_json_binary(&"uatom".to_string()).unwrap(), + }, + ]; + let result: NeutronResult = + BalancesAndDelegations::reconstruct(&storage_values); + match result { + Ok(balances_and_delegations) => { + let expected_coins = vec![cosmwasm_std::Coin { + denom: "uatom".to_string(), + amount: Uint128::from(1000u128), + }]; + assert_eq!(balances_and_delegations.balances.coins, expected_coins); + + let expected_delegations: Vec = vec![]; + assert_eq!( + balances_and_delegations.delegations.delegations, + expected_delegations + ); + } + Err(e) => { + panic!("reconstruct method returned an error: {:?}", e); + } + } +} + +#[test] +fn test_reconstruct_balance_and_delegations_with_delegations() { + let coin = cosmos_sdk_proto::cosmos::base::v1beta1::Coin { + denom: "uatom".to_string(), + amount: "1000".to_string(), + }; + let mut buf_coin = Vec::new(); + coin.encode(&mut buf_coin).unwrap(); + let mut storage_values: Vec = vec![ + StorageValue { + storage_prefix: "prefix".to_string(), + key: Binary::from("balances".as_bytes()), + value: buf_coin.into(), + }, + StorageValue { + storage_prefix: "prefix".to_string(), + key: Binary::from("denom".as_bytes()), + value: to_json_binary(&"uatom".to_string()).unwrap(), + }, + ]; + + let delegation = cosmos_sdk_proto::cosmos::staking::v1beta1::Delegation { + delegator_address: "delegator".to_string(), + validator_address: "validator".to_string(), + shares: "1000".to_string(), + }; + let mut buf = Vec::new(); + delegation.encode(&mut buf).unwrap(); + storage_values.push(StorageValue { + storage_prefix: "prefix".to_string(), + key: Binary::from("delegation".as_bytes()), + value: buf.into(), + }); + + let validator = cosmos_sdk_proto::cosmos::staking::v1beta1::Validator { + operator_address: "operator".to_string(), + consensus_pubkey: None, + jailed: false, + status: 1, + tokens: "1000".to_string(), + delegator_shares: "1000".to_string(), + description: None, + unbonding_height: 0, + unbonding_time: None, + commission: None, + min_self_delegation: "1000".to_string(), + }; + let mut buf = Vec::new(); + validator.encode(&mut buf).unwrap(); + storage_values.push(StorageValue { + storage_prefix: "prefix".to_string(), + key: Binary::from("validator".as_bytes()), + value: buf.into(), + }); + + let result: NeutronResult = + BalancesAndDelegations::reconstruct(&storage_values); + match result { + Ok(balances_and_delegations) => { + let expected_coins = vec![cosmwasm_std::Coin { + denom: "uatom".to_string(), + amount: Uint128::from(1000u128), + }]; + assert_eq!(balances_and_delegations.balances.coins, expected_coins); + + let expected_delegations: Vec = vec![Delegation { + delegator: Addr::unchecked("delegator"), + validator: "validator".to_string(), + amount: Coin { + denom: "uatom".to_string(), + amount: Uint128::from(1000u128), + }, + }]; + assert_eq!( + balances_and_delegations.delegations.delegations, + expected_delegations + ); + } + Err(e) => { + panic!("reconstruct method returned an error: {:?}", e); + } + } +}