Skip to content

Commit

Permalink
fix(integration_test): cucumber-rs improvements (#177)
Browse files Browse the repository at this point in the history
Description
---
* Validator nodes are now able to register (depends on [tari#4879](tari-project/tari#4879))
* Adding new cucumber steps to facilitate manual testing: printing node information and pausing execution

Motivation and Context
---
Previously, the cucumber integration test didn't allow VN registration, due to an invalid consensus constant in the `localnet` network.

Also, manual inspection (gRPC, json-rpc, database files, etc.) should be easier to do, so new cucumber steps are added to enable it.

Depends on [tari#4879](tari-project/tari#4879)

How Has This Been Tested?
---
The integration tests now allows registration of VNs and asserts that they are indeed registered. **The template function call does still fail, so it's commented out.**
  • Loading branch information
mrnaveira authored Nov 10, 2022
1 parent d922ef1 commit 562fcbe
Show file tree
Hide file tree
Showing 10 changed files with 302 additions and 293 deletions.
422 changes: 177 additions & 245 deletions Cargo.lock

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use std::{convert::TryInto, net::SocketAddr};

use async_trait::async_trait;
use log::trace;
use tari_app_grpc::tari_rpc::{self as grpc, GetCommitteeRequest, GetShardKeyRequest};
use tari_app_grpc::tari_rpc::{self as grpc, GetShardKeyRequest};
use tari_base_node_grpc_client::BaseNodeGrpcClient;
use tari_common_types::types::{FixedHash, PublicKey};
use tari_comms::types::CommsPublicKey;
Expand Down Expand Up @@ -130,20 +130,6 @@ impl BaseNodeClient for GrpcBaseNodeClient {
Ok(vns)
}

async fn get_committee(&mut self, height: u64, shard_key: &[u8; 32]) -> Result<Vec<CommsPublicKey>, BaseNodeError> {
let inner = self.connection().await?;
let request = GetCommitteeRequest {
height,
shard_key: shard_key.to_vec(),
};
let result = inner.get_committee(request).await?.into_inner();
Ok(result
.public_key
.iter()
.map(|a| CommsPublicKey::from_vec(a).unwrap())
.collect())
}

async fn get_shard_key(&mut self, height: u64, public_key: &PublicKey) -> Result<Option<ShardId>, BaseNodeError> {
let inner = self.connection().await?;
let request = GetShardKeyRequest {
Expand Down
80 changes: 68 additions & 12 deletions applications/tari_validator_node/tests/cucumber.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,15 @@ mod utils;

use std::{
convert::{Infallible, TryFrom},
io,
time::Duration,
};

use async_trait::async_trait;
use cucumber::{given, then, when, WorldInit};
use cucumber::{given, then, when, writer, WorldInit, WriterExt};
use indexmap::IndexMap;
use tari_common_types::types::PublicKey;
use tari_crypto::tari_utilities::hex::Hex;
use tari_dan_core::services::BaseNodeClient;
use tari_template_lib::Hash;
use tari_validator_node::GrpcBaseNodeClient;
Expand Down Expand Up @@ -131,19 +134,23 @@ async fn register_template(world: &mut TariWorld, vn_name: String, template_name

#[then(expr = "the validator node {word} is listed as registered")]
async fn assert_vn_is_registered(world: &mut TariWorld, vn_name: String) {
// create a base node client
let base_node_grpc_port = world.validator_nodes.get(&vn_name).unwrap().base_node_grpc_port;
let mut base_node_client: GrpcBaseNodeClient = get_base_node_client(base_node_grpc_port).await;

// get the list of registered vns from the base node
let height = base_node_client.get_tip_info().await.unwrap().height_of_longest_chain;
let vns = base_node_client.get_validator_nodes(height).await.unwrap();
// FIXME: the base node returns an empty list of registered vns, but the registration tx is actually in a block
assert!(!vns.is_empty());

let registered_vn = &vns[0];
// retrieve the VN's public key
let jrpc_port = world.validator_nodes.get(&vn_name).unwrap().json_rpc_port;
let mut client = get_vn_client(jrpc_port).await;
let identity: GetIdentityResponse = client.get_identity().await.unwrap();
assert_eq!(identity.public_key, registered_vn.public_key.to_string());
let public_key: PublicKey = PublicKey::from_hex(&identity.public_key).unwrap();

// check that the vn's public key is in the list of registered vns
assert!(vns.iter().any(|vn| vn.public_key == public_key));
}

#[then(expr = "the template \"{word}\" is listed as registered by the validator node {word}")]
Expand All @@ -169,19 +176,68 @@ async fn assert_valid_vn_identity(world: &mut TariWorld, vn_name: String) {
let mut client = get_vn_client(jrpc_port).await;
let resp = client.get_identity().await.unwrap();

println!("VN identity response: {:?}", resp);
assert!(!resp.public_key.is_empty());
}

#[then(
expr = "the validator node {word} calls the function \"{word}\" on the template \"{word}\" and gets a valid \
response"
)]
#[when(expr = "the validator node {word} calls the function \"{word}\" on the template \"{word}\"")]
async fn call_template_function(world: &mut TariWorld, vn_name: String, function_name: String, template_name: String) {
let _resp = send_template_transaction(world, vn_name, template_name, function_name).await;
let resp = send_template_transaction(world, vn_name, template_name, function_name).await;

eprintln!("Template call response: {:?}", resp);
}

#[when(expr = "I wait {int} seconds")]
async fn wait_seconds(_world: &mut TariWorld, seconds: u64) {
tokio::time::sleep(Duration::from_secs(seconds)).await;
}

#[when(expr = "I print the cucumber world")]
async fn print_world(world: &mut TariWorld) {
eprintln!();
eprintln!("======================================");
eprintln!("============= TEST NODES =============");
eprintln!("======================================");
eprintln!();

// base nodes
for (name, node) in world.base_nodes.iter() {
eprintln!(
"Base node \"{}\": grpc port \"{}\", temp dir path \"{}\"",
name, node.grpc_port, node.temp_dir_path
);
}

// wallets
for (name, node) in world.wallets.iter() {
eprintln!(
"Wallet \"{}\": grpc port \"{}\", temp dir path \"{}\"",
name, node.grpc_port, node.temp_dir_path
);
}

// vns
for (name, node) in world.validator_nodes.iter() {
eprintln!(
"Validator node \"{}\": json rpc port \"{}\", http ui port \"{}\", temp dir path \"{}\"",
name, node.json_rpc_port, node.http_ui_port, node.temp_dir_path
);
}

eprintln!();
eprintln!("======================================");
eprintln!();
}

#[tokio::main]
async fn main() {
// env_logger::init();
TariWorld::run("tests/features/").await;
TariWorld::cucumber()
// following config needed to use eprint statements in the tests
.max_concurrent_scenarios(1)
.with_writer(
writer::Basic::raw(io::stdout(), writer::Coloring::Never, 0)
.summarized()
.assert_normalized(),
)
.run_and_exit("tests/features/")
.await;
}
18 changes: 11 additions & 7 deletions applications/tari_validator_node/tests/features/basic.feature
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,22 @@ Feature: Basic scenarios
When validator node VAL_1 sends a registration transaction
When validator node VAL_2 sends a registration transaction
When miner MINER mines 20 new blocks
# FIXME: the following instructions fail due to the VNs not listed in the base node
# but the base node blocks do include the registration transactions
# Then the validator node VAL_1 is listed as registered
# Then the validator node VAL_2 is listed as registered
Then the validator node VAL_1 is listed as registered
Then the validator node VAL_2 is listed as registered

# Register the "counter" template
When validator node VAL_1 registers the template "counter"
When miner MINER mines 20 new blocks
Then the template "counter" is listed as registered by the validator node VAL_1
Then the template "counter" is listed as registered by the validator node VAL_2
# FIXME: In GitHub actions, we get a "Template not found" error in VN2
# Then the template "counter" is listed as registered by the validator node VAL_2

# Call the constructor in the "counter" template
# FIXME: the following instruction fails due to no epoch found
# Then the validator node VAL_1 calls the function "new" on the template "counter" and gets a valid response
# FIXME: The VN does not return a valid response
# When the validator node VAL_1 calls the function "new" on the template "counter"

# Uncomment the following lines to stop execution for manual inspection of the nodes
# When I print the cucumber world
# When I wait 1000 seconds


Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion applications/tari_validator_node/tests/utils/base_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ pub struct BaseNodeProcess {
pub grpc_port: u64,
pub identity: NodeIdentity,
pub handle: task::JoinHandle<()>,
pub temp_dir_path: String,
}

pub async fn spawn_base_node(world: &mut TariWorld, bn_name: String) {
Expand All @@ -53,6 +54,8 @@ pub async fn spawn_base_node(world: &mut TariWorld, bn_name: String) {
let base_node_identity = NodeIdentity::random(&mut OsRng, base_node_address, PeerFeatures::COMMUNICATION_NODE);
println!("Base node identity: {}", base_node_identity);
let identity = base_node_identity.clone();
let temp_dir = tempdir().unwrap();
let temp_dir_path = temp_dir.path().display().to_string();

let handle = task::spawn(async move {
let mut base_node_config = tari_base_node::ApplicationConfig {
Expand All @@ -63,7 +66,6 @@ pub async fn spawn_base_node(world: &mut TariWorld, bn_name: String) {
metrics: MetricsConfig::default(),
};

let temp_dir = tempdir().unwrap();
println!("Using base_node temp_dir: {}", temp_dir.path().display());
base_node_config.base_node.network = Network::LocalNet;
base_node_config.base_node.grpc_enabled = true;
Expand Down Expand Up @@ -96,6 +98,7 @@ pub async fn spawn_base_node(world: &mut TariWorld, bn_name: String) {
grpc_port,
identity,
handle,
temp_dir_path,
};
world.base_nodes.insert(bn_name, node_process);

Expand Down
2 changes: 1 addition & 1 deletion applications/tari_validator_node/tests/utils/template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ pub async fn send_template_transaction(
sender_public_key: transaction.sender_public_key().clone(),
wait_for_result: true,
inputs: vec![],
num_outputs: 1,
num_outputs: 0,
};

// send the template transaction request
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ pub struct ValidatorNodeProcess {
pub http_ui_port: u64,
pub base_node_grpc_port: u64,
pub handle: task::JoinHandle<()>,
pub temp_dir_path: String,
}

pub async fn spawn_validator_node(
Expand All @@ -60,6 +61,9 @@ pub async fn spawn_validator_node(
let wallet_grpc_port = world.wallets.get(&wallet_name).unwrap().grpc_port;
let name = validator_node_name.clone();

let temp_dir = tempdir().unwrap().path().join(validator_node_name.clone());
let temp_dir_path = temp_dir.display().to_string();

let handle = task::spawn(async move {
let mut config = ApplicationConfig {
common: CommonConfig::default(),
Expand Down Expand Up @@ -122,6 +126,7 @@ pub async fn spawn_validator_node(
http_ui_port,
handle,
json_rpc_port,
temp_dir_path,
};
world.validator_nodes.insert(name, validator_node_process);

Expand Down
5 changes: 4 additions & 1 deletion applications/tari_validator_node/tests/utils/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ pub struct WalletProcess {
pub port: u64,
pub grpc_port: u64,
pub handle: JoinHandle<()>,
pub temp_dir_path: String,
}

pub async fn spawn_wallet(world: &mut TariWorld, wallet_name: String, base_node_name: String) {
Expand All @@ -70,6 +71,8 @@ pub async fn spawn_wallet(world: &mut TariWorld, wallet_name: String, base_node_
net_address: format! {"/ip4/127.0.0.1/tcp/{}", base_node_port},
public_key_hex: base_node_public_key.clone().to_string(),
};
let temp_dir = tempdir().unwrap();
let temp_dir_path = temp_dir.path().display().to_string();

let handle = thread::spawn(move || {
let mut wallet_config = tari_console_wallet::ApplicationConfig {
Expand All @@ -79,7 +82,6 @@ pub async fn spawn_wallet(world: &mut TariWorld, wallet_name: String, base_node_
peer_seeds: PeerSeedsConfig::default(),
};

let temp_dir = tempdir().unwrap();
eprintln!("Using wallet temp_dir: {}", temp_dir.path().display());

wallet_config.wallet.network = Network::LocalNet;
Expand Down Expand Up @@ -117,6 +119,7 @@ pub async fn spawn_wallet(world: &mut TariWorld, wallet_name: String, base_node_
port,
grpc_port,
handle,
temp_dir_path,
};
world.wallets.insert(wallet_name.clone(), wallet_process);

Expand Down
2 changes: 0 additions & 2 deletions dan_layer/core/src/services/base_node_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@

use async_trait::async_trait;
use tari_common_types::types::{FixedHash, PublicKey};
use tari_comms::types::CommsPublicKey;
use tari_core::{
blocks::BlockHeader,
transactions::transaction_components::{CodeTemplateRegistration, TransactionOutput},
Expand All @@ -39,7 +38,6 @@ pub trait BaseNodeClient: Send + Sync + Clone {
async fn test_connection(&mut self) -> Result<(), BaseNodeError>;
async fn get_tip_info(&mut self) -> Result<BaseLayerMetadata, BaseNodeError>;
async fn get_validator_nodes(&mut self, height: u64) -> Result<Vec<ValidatorNode>, BaseNodeError>;
async fn get_committee(&mut self, height: u64, shard_key: &[u8; 32]) -> Result<Vec<CommsPublicKey>, BaseNodeError>;
async fn get_shard_key(&mut self, height: u64, public_key: &PublicKey) -> Result<Option<ShardId>, BaseNodeError>;
async fn get_template_registrations(
&mut self,
Expand Down

0 comments on commit 562fcbe

Please sign in to comment.