Skip to content

Commit

Permalink
test(cucumber): enable counter template scenario (#1136)
Browse files Browse the repository at this point in the history
  • Loading branch information
therealdannzor authored Sep 10, 2024
1 parent d983b36 commit 183c90f
Show file tree
Hide file tree
Showing 6 changed files with 243 additions and 78 deletions.
19 changes: 19 additions & 0 deletions integration_tests/src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ use std::{
time::Duration,
};

use tari_engine_types::substate::SubstateId;
use tokio::{io::AsyncWriteExt, task::JoinHandle};

use crate::TariWorld;

pub fn get_os_assigned_port() -> u16 {
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
listener.local_addr().unwrap().port()
Expand Down Expand Up @@ -117,3 +120,19 @@ pub async fn check_join_handle<E: Display>(
},
}
}

pub fn get_address_from_output(world: &TariWorld, output_ref: String) -> &SubstateId {
world
.outputs
.iter()
.find_map(|(parent_name, outputs)| {
outputs
.iter()
.find(|(child_name, _)| {
let fqn = format!("{}/{}", parent_name, child_name);
fqn == output_ref
})
.map(|(_, addr)| &addr.substate_id)
})
.unwrap_or_else(|| panic!("Output not found: {}", output_ref))
}
18 changes: 1 addition & 17 deletions integration_tests/src/indexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ use tari_template_lib::models::ObjectKey;
use tokio::task;

use crate::{
helpers::{check_join_handle, get_os_assigned_ports, wait_listener_on_local_port},
helpers::{check_join_handle, get_address_from_output, get_os_assigned_ports, wait_listener_on_local_port},
logging::get_base_dir_for_scenario,
TariWorld,
};
Expand Down Expand Up @@ -142,22 +142,6 @@ impl IndexerProcess {
}
}

fn get_address_from_output(world: &TariWorld, output_ref: String) -> &SubstateId {
world
.outputs
.iter()
.find_map(|(name, outputs)| {
outputs
.iter()
.find(|(child_name, _)| {
let fqn = format!("{}/{}", name, child_name);
fqn == output_ref
})
.map(|(_, addr)| &addr.substate_id)
})
.unwrap()
}

pub async fn spawn_indexer(world: &mut TariWorld, indexer_name: String, base_node_name: String) {
// each spawned indexer will use different ports
let (port, json_rpc_port) = get_os_assigned_ports();
Expand Down
108 changes: 103 additions & 5 deletions integration_tests/src/wallet_daemon_cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ use tari_crypto::{
tari_utilities::ByteArray,
};
use tari_dan_common_types::{Epoch, SubstateRequirement};
use tari_dan_wallet_sdk::apis::confidential_transfer::ConfidentialTransferInputSelection;
use tari_dan_wallet_sdk::{apis::confidential_transfer::ConfidentialTransferInputSelection, models::Account};
use tari_engine_types::instruction::Instruction;
use tari_template_lib::{
args,
Expand All @@ -39,7 +39,7 @@ use tari_template_lib::{
prelude::{ComponentAddress, ResourceAddress},
resource::TOKEN_SYMBOL,
};
use tari_transaction::Transaction;
use tari_transaction::{Transaction, UnsignedTransaction};
use tari_transaction_manifest::{parse_manifest, ManifestValue};
use tari_validator_node_cli::command::transaction::CliArg;
use tari_wallet_daemon_client::{
Expand Down Expand Up @@ -67,7 +67,7 @@ use tari_wallet_daemon_client::{
};
use tokio::time::timeout;

use crate::{validator_node_cli::add_substate_ids, TariWorld};
use crate::{helpers::get_address_from_output, validator_node_cli::add_substate_ids, TariWorld};

pub async fn claim_burn(
world: &mut TariWorld,
Expand Down Expand Up @@ -146,7 +146,6 @@ pub async fn reveal_burned_funds(world: &mut TariWorld, account_name: String, am
assert!(wait_resp.result.unwrap().result.is_accept());
}

#[allow(clippy::too_many_arguments)]
pub async fn transfer_confidential(
world: &mut TariWorld,
source_account_name: String,
Expand Down Expand Up @@ -605,7 +604,12 @@ pub async fn create_component(
let template_address = world
.templates
.get(&template_name)
.unwrap_or_else(|| panic!("Template not found with name {}", template_name))
.unwrap_or_else(|| {
panic!(
"Create component failed, template not found with name {}",
template_name
)
})
.address;
let args = args.iter().map(|a| CliArg::from_str(a).unwrap().into_arg()).collect();
let AccountGetResponse { account, .. } = client
Expand Down Expand Up @@ -656,6 +660,48 @@ pub async fn create_component(
);
}

pub async fn call_component(
world: &mut TariWorld,
account_name: String,
output_ref: String,
wallet_daemon_name: String,
function_call: String,
) -> anyhow::Result<TransactionWaitResultResponse> {
let mut client = get_auth_wallet_daemon_client(world, &wallet_daemon_name).await;

let source_component_address = get_address_from_output(world, output_ref.clone())
.as_component_address()
.expect("Failed to get component address from output");

let account = get_account_from_name(&mut client, account_name).await;
let account_component_address = account
.address
.as_component_address()
.expect("Failed to get account component address");

let tx = Transaction::builder()
.fee_transaction_pay_from_component(account_component_address, Amount(1))
.call_method(source_component_address, &function_call, vec![])
.build_unsigned_transaction();

let resp = submit_unsigned_tx_and_wait_for_response(&mut client, tx, vec![], account).await;

add_substate_ids(
world,
output_ref,
&resp
.as_ref()
.unwrap()
.clone()
.result
.expect("Call component transaction has timed out")
.result
.expect("Call component transaction has failed"),
);

resp
}

pub async fn transfer(
world: &mut TariWorld,
account_name: String,
Expand Down Expand Up @@ -721,3 +767,55 @@ pub async fn get_auth_wallet_daemon_client(world: &TariWorld, wallet_daemon_name
.get_authed_client()
.await
}

async fn get_account_from_name(client: &mut WalletDaemonClient, account_name: String) -> Account {
let source_account_name = ComponentAddressOrName::Name(account_name.clone());
let AccountGetResponse { account, .. } =
client
.accounts_get(source_account_name.clone())
.await
.unwrap_or_else(|e| {
panic!(
"Failed to get account with name {}. Error: {:?}",
source_account_name, e
)
});
account
}

async fn submit_unsigned_tx_and_wait_for_response(
client: &mut WalletDaemonClient,
tx: UnsignedTransaction,
inputs: Vec<SubstateRequirement>,
account: Account,
) -> anyhow::Result<TransactionWaitResultResponse> {
let submit_req = TransactionSubmitRequest {
transaction: Some(tx),
signing_key_index: Some(account.key_index),
inputs,
override_inputs: false,
is_dry_run: false,
proof_ids: vec![],
// TODO: remove
fee_instructions: vec![],
instructions: vec![],
min_epoch: None,
max_epoch: None,
};

let submit_resp = client.submit_transaction(submit_req).await?;
let wait_req = TransactionWaitResultRequest {
transaction_id: submit_resp.transaction_id,
timeout_secs: Some(120),
};
let resp = client
.wait_transaction_result(wait_req)
.await
.unwrap_or_else(|_| panic!("Waiting for the transaction when calling component failed"));

if let Some(reason) = resp.result.clone().and_then(|finalize| finalize.reject().cloned()) {
panic!("Calling component result rejected: {}", reason);
}

Ok(resp)
}
70 changes: 70 additions & 0 deletions integration_tests/tests/cucumber.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,30 @@ async fn call_template_constructor_via_wallet_daemon(
// tokio::time::sleep(Duration::from_secs(4)).await;
}

#[when(
expr = r#"I call function "{word}" on template "{word}" using account {word} to pay fees via wallet daemon {word} named "{word}""#
)]
async fn call_template_constructor_via_wallet_daemon_no_args(
world: &mut TariWorld,
function_call: String,
template_name: String,
account_name: String,
wallet_daemon_name: String,
outputs_name: String,
) {
wallet_daemon_cli::create_component(
world,
outputs_name,
template_name,
account_name,
wallet_daemon_name,
function_call,
vec![],
None,
None,
)
.await;
}
#[when(expr = r#"I call function "{word}" on template "{word}" on {word} with args "{word}" named "{word}""#)]
async fn call_template_constructor(
world: &mut TariWorld,
Expand Down Expand Up @@ -302,6 +326,52 @@ async fn call_component_method_and_check_result(
// tokio::time::sleep(Duration::from_secs(4)).await;
}

#[when(
expr = r#"I invoke on wallet daemon {word} on account {word} on component {word} the method call "{word}" the result is "{word}""#
)]
async fn call_wallet_daemon_method_and_check_result(
world: &mut TariWorld,
wallet_daemon_name: String,
account_name: String,
output_ref: String,
method_call: String,
expected_result: String,
) -> anyhow::Result<()> {
let resp =
wallet_daemon_cli::call_component(world, account_name, output_ref, wallet_daemon_name, method_call).await?;

let finalize_result = resp
.result
.clone()
.unwrap_or_else(|| panic!("Failed to unwrap result from response: {:?}", resp));
let result = finalize_result
.execution_results
.first()
.unwrap_or_else(|| panic!("Failed to call first() on results: {:?}", resp));
match result.return_type {
Type::U32 => {
let u32_result: u32 = result.decode().unwrap();
assert_eq!(u32_result.to_string(), expected_result);
},
_ => todo!(),
};

Ok(())
}

#[when(expr = r#"I invoke on wallet daemon {word} on account {word} on component {word} the method call "{word}""#)]
async fn call_wallet_daemon_method(
world: &mut TariWorld,
wallet_daemon_name: String,
account_name: String,
output_ref: String,
method_call: String,
) -> anyhow::Result<()> {
wallet_daemon_cli::call_component(world, account_name, output_ref, wallet_daemon_name, method_call).await?;

Ok(())
}

#[when(
expr = "I invoke on all validator nodes on component {word} the method call \"{word}\" the result is \"{word}\""
)]
Expand Down
50 changes: 50 additions & 0 deletions integration_tests/tests/features/counter.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Copyright 2024 The Tari Project
# SPDX-License-Identifier: BSD-3-Clause

@counter
Feature: Counter template

@serial
Scenario: Counter template registration and invocation

# Initialize a base node, wallet, miner and VN
Given fees are disabled
Given a base node BASE
Given a wallet WALLET connected to base node BASE
Given a miner MINER connected to base node BASE and wallet WALLET

# Initialize a VN
Given a validator node VAL connected to base node BASE and wallet daemon WALLET_D

# Fund wallet to send VN registration tx
When miner MINER mines 10 new blocks
When wallet WALLET has at least 2000 T
When validator node VAL sends a registration transaction to base wallet WALLET
When miner MINER mines 16 new blocks
Then the validator node VAL is listed as registered

# Initialize indexer and connect wallet daemon
Given an indexer IDX connected to base node BASE
Given a wallet daemon WALLET_D connected to indexer IDX

# Register the "counter" template
When base wallet WALLET registers the template "counter"
When miner MINER mines 20 new blocks
Then VAL has scanned to height 43

# Create the sender account
When I create an account ACC via the wallet daemon WALLET_D with 10000 free coins

# The initial value of the counter must be 0
When I call function "new" on template "counter" using account ACC to pay fees via wallet daemon WALLET_D named "COUNTER"
When I invoke on wallet daemon WALLET_D on account ACC on component COUNTER/components/Counter the method call "value" the result is "0"

# Increase the counter
When I invoke on wallet daemon WALLET_D on account ACC on component COUNTER/components/Counter the method call "increase"

# Check that the counter has been increased
When I invoke on wallet daemon WALLET_D on account ACC on component COUNTER/components/Counter the method call "value" the result is "1"

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

0 comments on commit 183c90f

Please sign in to comment.