From be0e67f64ceeb58129f1a76b4e633fce456cc805 Mon Sep 17 00:00:00 2001 From: Vladislav Date: Wed, 13 Mar 2024 20:28:09 +0300 Subject: [PATCH] [feature] #4116: Add test for plain == scale encoded verification for status request (#4284) * [feature] add test for json == scale verification Signed-off-by: VAmuzing --- client/src/client.rs | 16 ++++-- client/src/lib.rs | 14 +++++ client/tests/integration/mod.rs | 1 + client/tests/integration/status_response.rs | 57 +++++++++++++++++++++ telemetry/src/metrics.rs | 15 +++++- 5 files changed, 97 insertions(+), 6 deletions(-) create mode 100644 client/tests/integration/status_response.rs diff --git a/client/src/client.rs b/client/src/client.rs index a9636e7866e..789f6ef7a76 100644 --- a/client/src/client.rs +++ b/client/src/client.rs @@ -175,13 +175,18 @@ impl TransactionResponseHandler { pub struct StatusResponseHandler; impl StatusResponseHandler { - fn handle(resp: &Response>) -> Result { + pub(crate) fn handle(resp: &Response>) -> Result { + let slice = Self::handle_raw(resp)?; + serde_json::from_slice(slice).wrap_err("Failed to decode body") + } + + fn handle_raw(resp: &Response>) -> Result<&Vec> { if resp.status() != StatusCode::OK { return Err(ResponseReport::with_msg("Unexpected status response", resp) .unwrap_or_else(core::convert::identity) .into()); } - serde_json::from_slice(resp.body()).wrap_err("Failed to decode body") + Ok(resp.body()) } } @@ -1029,9 +1034,12 @@ impl Client { /// # Errors /// Fails if sending request or decoding fails pub fn get_status(&self) -> Result { - let req = self.prepare_status_request::(); + let req = self + .prepare_status_request::() + .header(http::header::ACCEPT, "application/x-parity-scale"); let resp = req.build()?.send()?; - StatusResponseHandler::handle(&resp) + let scaled_resp = StatusResponseHandler::handle_raw(&resp).cloned()?; + DecodeAll::decode_all(&mut scaled_resp.as_slice()).map_err(|err| eyre!("{err}")) } /// Prepares http-request to implement [`Self::get_status`] on your own. diff --git a/client/src/lib.rs b/client/src/lib.rs index 3ccb8fdb45c..fc518a0cf6a 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -10,15 +10,19 @@ mod query_builder; /// Module containing sample configurations for tests and benchmarks. pub mod samples { + use eyre::Result; + use iroha_telemetry::metrics::Status; use url::Url; use crate::{ + client::{Client, StatusResponseHandler}, config::{ Config, DEFAULT_TRANSACTION_NONCE, DEFAULT_TRANSACTION_STATUS_TIMEOUT, DEFAULT_TRANSACTION_TIME_TO_LIVE, }, crypto::KeyPair, data_model::ChainId, + http_default::DefaultRequestBuilder, }; /// Get sample client configuration. @@ -36,6 +40,16 @@ pub mod samples { transaction_add_nonce: DEFAULT_TRANSACTION_NONCE, } } + + /// Gets network status seen from the peer in json format + /// + /// # Errors + /// Fails if sending request or decoding fails + pub fn get_status_json(client: &Client) -> Result { + let req = client.prepare_status_request::(); + let resp = req.build()?.send()?; + StatusResponseHandler::handle(&resp) + } } pub use iroha_crypto as crypto; diff --git a/client/tests/integration/mod.rs b/client/tests/integration/mod.rs index ac94bb4e1c5..98b17659895 100644 --- a/client/tests/integration/mod.rs +++ b/client/tests/integration/mod.rs @@ -18,6 +18,7 @@ mod queries; mod roles; mod set_parameter; mod sorting; +mod status_response; mod transfer_asset; mod triggers; mod tx_chain_id; diff --git a/client/tests/integration/status_response.rs b/client/tests/integration/status_response.rs new file mode 100644 index 00000000000..a0bf6c87d97 --- /dev/null +++ b/client/tests/integration/status_response.rs @@ -0,0 +1,57 @@ +use std::str::FromStr as _; + +use eyre::Result; +use iroha_client::{data_model::prelude::*, samples::get_status_json}; +use iroha_telemetry::metrics::Status; +use test_network::*; + +fn status_eq_excluding_uptime_and_queue(lhs: &Status, rhs: &Status) -> bool { + lhs.peers == rhs.peers + && lhs.blocks == rhs.blocks + && lhs.txs_accepted == rhs.txs_accepted + && lhs.txs_rejected == rhs.txs_rejected + && lhs.view_changes == rhs.view_changes +} + +#[test] +fn json_and_scale_statuses_equality() -> Result<()> { + let (_rt, network, client) = Network::start_test_with_runtime(2, Some(11_200)); + wait_for_genesis_committed(&network.clients(), 0); + + let json_status_zero = get_status_json(&client).unwrap(); + + let scale_status_zero_decoded = client.get_status().unwrap(); + + assert!( + status_eq_excluding_uptime_and_queue(&json_status_zero, &scale_status_zero_decoded), + "get_status() result is not equal to decoded get_status_scale_encoded()" + ); + + let coins = ["xor", "btc", "eth", "doge"]; + + let domain_id: DomainId = "test_domain".parse().expect("Should be valid"); + let account_id = AccountId::new(domain_id, "test_account".parse().expect("Should be valid")); + + for coin in coins { + let asset_definition_id = AssetDefinitionId::from_str(&format!("{coin}#wonderland"))?; + let create_asset = + Register::asset_definition(AssetDefinition::numeric(asset_definition_id.clone())); + let mint_asset = Mint::asset_numeric( + 1234u32, + AssetId::new(asset_definition_id, account_id.clone()), + ); + let instructions: [InstructionBox; 2] = [create_asset.into(), mint_asset.into()]; + client.submit_all(instructions)?; + } + + let json_status_coins = get_status_json(&client).unwrap(); + + let scale_status_coins_decoded = client.get_status().unwrap(); + + assert!( + status_eq_excluding_uptime_and_queue(&json_status_coins, &scale_status_coins_decoded), + "get_status() result is not equal to decoded get_status_scale_encoded()" + ); + + Ok(()) +} diff --git a/telemetry/src/metrics.rs b/telemetry/src/metrics.rs index ad4f7744750..404e32c3916 100644 --- a/telemetry/src/metrics.rs +++ b/telemetry/src/metrics.rs @@ -5,7 +5,7 @@ use std::{ time::{Duration, SystemTime}, }; -use parity_scale_codec::{Compact, Encode}; +use parity_scale_codec::{Compact, Decode, Encode}; use prometheus::{ core::{AtomicU64, GenericGauge, GenericGaugeVec}, Encoder, Histogram, HistogramOpts, HistogramVec, IntCounter, IntCounterVec, Opts, Registry, @@ -32,8 +32,19 @@ impl Encode for Uptime { } } +impl Decode for Uptime { + fn decode( + input: &mut I, + ) -> Result { + let (secs, nanos) = <(Compact, u32)>::decode(input)?; + Ok(Self( + Duration::from_secs(secs.0) + Duration::from_nanos(nanos.into()), + )) + } +} + /// Response body for GET status request -#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, Encode)] +#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, Encode, Decode)] pub struct Status { /// Number of currently connected peers excluding the reporting peer #[codec(compact)]