From 6d6b1bb9581f161ce2a99d83da8746b338c297aa Mon Sep 17 00:00:00 2001 From: Stefano Cunego <93382903+kukkok3@users.noreply.github.com> Date: Thu, 1 Sep 2022 12:01:18 +0200 Subject: [PATCH] NPG 1943 explorer address test (#4078) * first address test * adding transaction by address tests * adding more checks * adding more comments * adding test for block0 * fix clippy and fmt * fix clippy * fix ending line * fix white space * fix clippy --- explorer/src/api/graphql/mod.rs | 2 +- explorer/src/logging.rs | 1 + jormungandr/src/blockchain/process.rs | 3 +- jormungandr/src/jrpc/eth_filter/filters.rs | 2 +- jormungandr/src/settings/logging.rs | 2 +- .../graphql/transactions_by_address.graphql | 21 ++ .../src/jormungandr/explorer/data.rs | 8 + .../src/jormungandr/explorer/mod.rs | 19 +- .../src/jormungandr/explorer/verifier.rs | 46 ++++- .../src/jormungandr/explorer/address.rs | 185 ++++++++++++++++-- 10 files changed, 268 insertions(+), 21 deletions(-) create mode 100644 testing/jormungandr-automation/resources/explorer/graphql/transactions_by_address.graphql diff --git a/explorer/src/api/graphql/mod.rs b/explorer/src/api/graphql/mod.rs index da936ffa6e..6266526b26 100644 --- a/explorer/src/api/graphql/mod.rs +++ b/explorer/src/api/graphql/mod.rs @@ -748,7 +748,7 @@ impl Transaction { .await; if block_hashes.is_empty() { - return Err(ApiError::NotFound(format!("transaction not found: {}", &id,)).into()); + Err(ApiError::NotFound(format!("transaction not found: {}", &id,)).into()) } else { Ok(Transaction { id, diff --git a/explorer/src/logging.rs b/explorer/src/logging.rs index 75c22199ca..43271d3e7c 100644 --- a/explorer/src/logging.rs +++ b/explorer/src/logging.rs @@ -23,6 +23,7 @@ pub struct LogSettings { /// some code executes before the logs are initialized. pub type LogInfoMsg = Option>; +#[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, Debug, PartialEq)] pub struct LogSettingsEntry { pub level: LevelFilter, diff --git a/jormungandr/src/blockchain/process.rs b/jormungandr/src/blockchain/process.rs index f021c9c0c6..11011b3596 100644 --- a/jormungandr/src/blockchain/process.rs +++ b/jormungandr/src/blockchain/process.rs @@ -528,8 +528,7 @@ async fn process_network_block( Err(Error::MissingParentBlock(parent_hash)) } PreCheckedHeader::HeaderWithCache { parent_ref, .. } => { - let r = check_and_apply_block(blockchain, parent_ref, block, watch_msg_box).await; - r + check_and_apply_block(blockchain, parent_ref, block, watch_msg_box).await } } } diff --git a/jormungandr/src/jrpc/eth_filter/filters.rs b/jormungandr/src/jrpc/eth_filter/filters.rs index 9766ac57a1..b286bc6eb3 100644 --- a/jormungandr/src/jrpc/eth_filter/filters.rs +++ b/jormungandr/src/jrpc/eth_filter/filters.rs @@ -23,7 +23,7 @@ impl EvmFilters { } } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq)] pub enum FilterType { Block, PendingTransaction, diff --git a/jormungandr/src/settings/logging.rs b/jormungandr/src/settings/logging.rs index 17e5adf5ea..740a4af890 100644 --- a/jormungandr/src/settings/logging.rs +++ b/jormungandr/src/settings/logging.rs @@ -21,7 +21,7 @@ pub struct LogSettings { /// some code executes before the logs are initialized. pub type LogInfoMsg = Option>; -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct LogSettingsEntry { pub level: LevelFilter, pub format: LogFormat, diff --git a/testing/jormungandr-automation/resources/explorer/graphql/transactions_by_address.graphql b/testing/jormungandr-automation/resources/explorer/graphql/transactions_by_address.graphql new file mode 100644 index 0000000000..7a92eb1654 --- /dev/null +++ b/testing/jormungandr-automation/resources/explorer/graphql/transactions_by_address.graphql @@ -0,0 +1,21 @@ +query TransactionsByAddress($bech32: String!){ + tip { + transactionsByAddress(addressBech32: $bech32) { + totalCount + edges { + node { + id + blocks{id date{...blockDate}} + #inputs{amount address{id}} // BUG NPG-2869 + #outputs{amount address{id}} + #certificate + } + } + } + } +} + +fragment blockDate on BlockDate{ + epoch{id} + slot + } diff --git a/testing/jormungandr-automation/src/jormungandr/explorer/data.rs b/testing/jormungandr-automation/src/jormungandr/explorer/data.rs index 04fc236d37..1fe3180f7f 100644 --- a/testing/jormungandr-automation/src/jormungandr/explorer/data.rs +++ b/testing/jormungandr-automation/src/jormungandr/explorer/data.rs @@ -19,6 +19,14 @@ use graphql_client::GraphQLQuery; )] pub struct Address; +#[derive(GraphQLQuery)] +#[graphql( + query_path = "resources/explorer/graphql/transactions_by_address.graphql", + schema_path = "resources/explorer/graphql/schema.graphql", + response_derives = "Debug" +)] +pub struct TransactionsByAddress; + #[derive(GraphQLQuery)] #[graphql( query_path = "resources/explorer/graphql/allblocks.graphql", diff --git a/testing/jormungandr-automation/src/jormungandr/explorer/mod.rs b/testing/jormungandr-automation/src/jormungandr/explorer/mod.rs index e207cb073b..d2a2e96573 100644 --- a/testing/jormungandr-automation/src/jormungandr/explorer/mod.rs +++ b/testing/jormungandr-automation/src/jormungandr/explorer/mod.rs @@ -4,8 +4,9 @@ use self::{ data::{ address, all_blocks, all_stake_pools, all_vote_plans, blocks_by_chain_length, epoch, last_block, settings, stake_pool, transaction_by_id, transaction_by_id_certificates, - Address, AllBlocks, AllStakePools, AllVotePlans, BlocksByChainLength, Epoch, LastBlock, - Settings, StakePool, TransactionById, TransactionByIdCertificates, + transactions_by_address, Address, AllBlocks, AllStakePools, AllVotePlans, + BlocksByChainLength, Epoch, LastBlock, Settings, StakePool, TransactionById, + TransactionByIdCertificates, TransactionsByAddress, }, }; use crate::testing::configuration::get_explorer_app; @@ -325,6 +326,20 @@ impl Explorer { Ok(response_body) } + pub fn transactions_address>( + &self, + bech32_address: S, + ) -> Result, ExplorerError> { + let query = TransactionsByAddress::build_query(transactions_by_address::Variables { + bech32: bech32_address.into(), + }); + self.print_request(&query); + let response = self.client.run(query).map_err(ExplorerError::ClientError)?; + let response_body: Response = response.json()?; + self.print_log(&response_body); + Ok(response_body) + } + pub fn current_time(&self) -> BlockDate { self.last_block().unwrap().block_date() } diff --git a/testing/jormungandr-automation/src/jormungandr/explorer/verifier.rs b/testing/jormungandr-automation/src/jormungandr/explorer/verifier.rs index a343114236..086f1b4f2f 100644 --- a/testing/jormungandr-automation/src/jormungandr/explorer/verifier.rs +++ b/testing/jormungandr-automation/src/jormungandr/explorer/verifier.rs @@ -1,4 +1,7 @@ -use super::data::{settings::SettingsSettingsFees, transaction_by_id_certificates::*}; +use super::data::{ + address::AddressAddress, settings::SettingsSettingsFees, transaction_by_id_certificates::*, + transactions_by_address::TransactionsByAddressTipTransactionsByAddress, +}; use crate::jormungandr::explorer::data::transaction_by_id_certificates::PayloadType as expPayloadType; use bech32::FromBase32; use chain_addr::AddressReadable; @@ -13,7 +16,8 @@ use chain_impl_mockchain::{ transaction::{AccountIdentifier, InputEnum, Transaction}, vote::PayloadType, }; -use std::num::NonZeroU64; +use jormungandr_lib::interfaces::{Address, FragmentStatus}; +use std::{collections::HashMap, num::NonZeroU64}; use thiserror::Error; #[derive(Debug, Error)] @@ -916,6 +920,44 @@ impl ExplorerVerifier { ); } + pub fn assert_address(address: Address, explorer_address: AddressAddress) { + assert_eq!(address.to_string(), explorer_address.id); + } + + pub fn assert_transactions_address( + fragment_statuses: HashMap, + explorer_transactions: TransactionsByAddressTipTransactionsByAddress, + ) { + if fragment_statuses.is_empty() { + assert!(explorer_transactions.total_count == 0); + } else { + assert_eq!( + fragment_statuses.len() as i64 + 1, + explorer_transactions.total_count + ); + }; + + assert!(explorer_transactions.edges.is_some()); + + assert_eq!( + fragment_statuses.len(), + explorer_transactions.edges.as_ref().unwrap().len() + ); + + for edges in explorer_transactions.edges.unwrap().iter() { + let node = &edges.as_ref().unwrap().node; + assert!(fragment_statuses.get(&node.id.to_string()).is_some()); + let fragment_status = fragment_statuses.get(&node.id.to_string()).unwrap().1; + assert!( + matches!(fragment_status, FragmentStatus::InABlock { date, block: _ } if + date.epoch() == node.blocks[0].date.epoch.id.parse::().unwrap() && date.slot() == node.blocks[0].date.slot.parse::().unwrap() + ) + ); + let fragment = fragment_statuses.get(&node.id.to_string()).unwrap().0; + assert_eq!(fragment.hash().to_string(), node.id.to_string()); + } + } + fn decode_bech32_pk(bech32_public_key: &str) -> PublicKey { let (_, data, _variant) = bech32::decode(bech32_public_key).unwrap(); let dat = Vec::from_base32(&data).unwrap(); diff --git a/testing/jormungandr-integration-tests/src/jormungandr/explorer/address.rs b/testing/jormungandr-integration-tests/src/jormungandr/explorer/address.rs index a9690eb57d..553e163929 100644 --- a/testing/jormungandr-integration-tests/src/jormungandr/explorer/address.rs +++ b/testing/jormungandr-integration-tests/src/jormungandr/explorer/address.rs @@ -1,18 +1,22 @@ use crate::startup; +use assert_fs::TempDir; +use chain_impl_mockchain::{block::BlockDate, fragment::Fragment}; use jormungandr_automation::{ jcli::JCli, - jormungandr::{explorer::configuration::ExplorerParams, ConfigurationBuilder}, + jormungandr::{ + explorer::{configuration::ExplorerParams, verifier::ExplorerVerifier}, + ConfigurationBuilder, Starter, + }, }; -use jormungandr_lib::interfaces::ActiveSlotCoefficient; -//TODO still wip -#[ignore] +use jormungandr_lib::interfaces::{ActiveSlotCoefficient, FragmentStatus}; +use jortestkit::process::Wait; +use std::{collections::HashMap, time::Duration}; +use thor::TransactionHash; + #[test] pub fn explorer_address_test() { - let _jcli: JCli = Default::default(); let sender = thor::Wallet::default(); - let _receiver = thor::Wallet::default(); - let _transaction_value = 1_000; - let address_bech32_prefix = "ca".to_string(); + let address_bech32_prefix = sender.address().0; let mut config = ConfigurationBuilder::new(); config.with_consensus_genesis_praos_active_slot_coeff(ActiveSlotCoefficient::MAXIMUM); @@ -32,9 +36,166 @@ pub fn explorer_address_test() { explorer_address.errors.unwrap() ); - assert_eq!( - explorer_address.data.unwrap().address.id, - sender.address().to_string(), - "Addresses is not the same" + ExplorerVerifier::assert_address(sender.address(), explorer_address.data.unwrap().address); +} + +#[test] +pub fn explorer_transactions_not_existing_address_test() { + let jcli: JCli = Default::default(); + let sender = thor::Wallet::default(); + let receiver = thor::Wallet::default(); + let test_address = thor::Wallet::default(); + let transaction_value = 1_000; + let attempts_number = 20; + + let mut config = ConfigurationBuilder::new(); + config.with_consensus_genesis_praos_active_slot_coeff(ActiveSlotCoefficient::MAXIMUM); + + let (jormungandr, _initial_stake_pools) = + startup::start_stake_pool(&[sender.clone()], &[sender.clone()], &mut config).unwrap(); + + let transaction = thor::FragmentBuilder::new( + &jormungandr.genesis_block_hash(), + &jormungandr.fees(), + BlockDate::first().next_epoch(), + ) + .transaction(&sender, receiver.address(), transaction_value.into()) + .unwrap(); + + let wait = Wait::new(Duration::from_secs(3), attempts_number); + + jcli.fragment_sender(&jormungandr) + .send(&transaction.encode()) + .assert_in_block_with_wait(&wait); + + let explorer_process = jormungandr.explorer(ExplorerParams::default()); + let explorer = explorer_process.client(); + + let explorer_address = explorer + .transactions_address(test_address.address().to_string()) + .unwrap(); + + assert!( + explorer_address.errors.is_none(), + "{:?}", + explorer_address.errors.unwrap() + ); + let explorer_transactions_by_address = + explorer_address.data.unwrap().tip.transactions_by_address; + + ExplorerVerifier::assert_transactions_address(HashMap::new(), explorer_transactions_by_address); +} + +// BUG NPG-2869 +// TODO comment out the fields (inputs,outputs, certificate) in transaction_by_address.graphql when the bug is fixed +// add the verifier for those fields (inputs,outputs,certificate) in explorer_verifier +#[test] +pub fn explorer_transactions_address_test() { + let jcli: JCli = Default::default(); + let mut sender = thor::Wallet::default(); + let receiver = thor::Wallet::default(); + let transaction1_value = 1_000; + let transaction2_value = 2_0; + let transaction3_value = 3_0; + let attempts_number = 20; + let temp_dir = TempDir::new().unwrap(); + let mut fragments = vec![]; + + let config = ConfigurationBuilder::default() + .with_funds(vec![sender.to_initial_fund(1_000_000)]) + .build(&temp_dir); + + let jormungandr = Starter::new() + .temp_dir(temp_dir) + .config(config) + .start() + .expect("Cannot start jormungandr"); + + let wait = Wait::new(Duration::from_secs(3), attempts_number); + + let fragment_builder = thor::FragmentBuilder::new( + &jormungandr.genesis_block_hash(), + &jormungandr.fees(), + BlockDate::first().next_epoch(), + ); + + let transaction_1 = fragment_builder + .transaction(&sender, receiver.address(), transaction1_value.into()) + .unwrap(); + + jcli.fragment_sender(&jormungandr) + .send(&transaction_1.encode()) + .assert_in_block_with_wait(&wait); + + fragments.push(&transaction_1); + + sender.confirm_transaction(); + + let transaction_2 = fragment_builder + .transaction(&sender, receiver.address(), transaction2_value.into()) + .unwrap(); + + jcli.fragment_sender(&jormungandr) + .send(&transaction_2.encode()) + .assert_in_block_with_wait(&wait); + + fragments.push(&transaction_2); + + let transaction_3 = fragment_builder + .transaction(&receiver, sender.address(), transaction3_value.into()) + .unwrap(); + + jcli.fragment_sender(&jormungandr) + .send(&transaction_3.encode()) + .assert_in_block_with_wait(&wait); + + fragments.push(&transaction_3); + + let mut fragments_log = jcli.rest().v0().message().logs(jormungandr.rest_uri()); + + fragments_log.sort(); + fragments.sort_by_key(|a| a.hash()); + + // make and hashmap of tuples of fragment and fragment status + let mut fragments_statuses: HashMap<_, _> = fragments + .iter() + .zip(fragments_log.iter()) + .map(|(&a, b)| (a.hash().to_string(), (a, b.status()))) + .collect(); + + let block0 = jormungandr.block0_configuration().to_block(); + let block0fragment: &Fragment = block0.fragments().last().unwrap(); + let block0_fragment_status = FragmentStatus::InABlock { + date: block0.header().block_date().into(), + block: block0.header().block_content_hash().into(), + }; + fragments_statuses.insert( + block0fragment.hash().to_string(), + (block0fragment, &block0_fragment_status), + ); + + let explorer_process = jormungandr.explorer(ExplorerParams::default()); + let explorer = explorer_process.client(); + + assert!(explorer + .transactions_address(sender.address().to_string()) + .is_ok()); + + let explorer_address = explorer + .transactions_address(sender.address().to_string()) + .unwrap(); + + assert!( + explorer_address.errors.is_none(), + "{:?}", + explorer_address.errors.unwrap() + ); + + let explorer_transactions_by_address = + explorer_address.data.unwrap().tip.transactions_by_address; + + ExplorerVerifier::assert_transactions_address( + fragments_statuses, + explorer_transactions_by_address, ); }