Skip to content

Commit

Permalink
test(extrinsics): write tests for the API
Browse files Browse the repository at this point in the history
Signed-off-by: Tarek <[email protected]>
  • Loading branch information
tareknaser committed Aug 15, 2023
1 parent e9cee00 commit 1d4348d
Show file tree
Hide file tree
Showing 2 changed files with 191 additions and 2 deletions.
4 changes: 2 additions & 2 deletions crates/extrinsics/src/instantiate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,9 @@ impl<E> InstantiateCommandBuilder<E> {
}

/// Sets the constructor arguments.
pub fn args(self, args: Vec<String>) -> Self {
pub fn args<T: ToString>(self, args: Vec<T>) -> Self {
let mut this = self;
this.opts.args = args;
this.opts.args = args.into_iter().map(|arg| arg.to_string()).collect();
this
}

Expand Down
189 changes: 189 additions & 0 deletions crates/extrinsics/src/integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@
// You should have received a copy of the GNU General Public License
// along with cargo-contract. If not, see <http://www.gnu.org/licenses/>.

use crate::{
CallCommand,
ExtrinsicOpts,
InstantiateCommand,
RemoveCommand,
UploadCommand,
};
use anyhow::Result;
use predicates::prelude::*;
use std::{
Expand Down Expand Up @@ -374,3 +381,185 @@ async fn build_upload_instantiate_info() {
// prevent the node_process from being dropped and killed
let _ = node_process;
}

/// This test uses contract extrinsics API to test the whole lifecycle of:
/// new -> build -> upload -> instantiate -> call
///
/// # Note
///
/// Requires [`substrate-contracts-node`](https://github.com/paritytech/substrate-contracts-node/) to
/// be installed and available on the `PATH`, and the no other process running using the
/// default port `9944`.
#[tokio::test]
async fn api_build_upload_instantiate_call() {
init_tracing_subscriber();

let tmp_dir = tempfile::Builder::new()
.prefix("cargo-contract.cli.test.")
.tempdir()
.expect("temporary directory creation failed");

let node_process = ContractsNodeProcess::spawn(CONTRACTS_NODE)
.await
.expect("Error spawning contracts node");

cargo_contract(tmp_dir.path())
.arg("new")
.arg("flipper")
.assert()
.success();

let mut project_path = tmp_dir.path().to_path_buf();
project_path.push("flipper");

cargo_contract(project_path.as_path())
.arg("build")
.assert()
.success();

// construct the contract file path
let contract_file = project_path.join("target/ink/flipper.contract");

// upload the contract
let opts = ExtrinsicOpts::new()
.file(contract_file)
.suri("//Alice")
.done();
let upload = UploadCommand::new()
.extrinsic_opts(opts.clone())
.done()
.await;
let upload_result = upload.upload_code().await;
assert!(upload_result.is_ok(), "upload code failed");
upload_result.unwrap();

// instantiate the contract
let instantiate = InstantiateCommand::new()
.extrinsic_opts(opts.clone())
.constructor("new")
.args(["true"].to_vec())
.done()
.await;
let instantiate_result = instantiate.instantiate(None).await;
assert!(instantiate_result.is_ok(), "instantiate code failed");
let instantiate_result = instantiate_result.unwrap();
let contract_account = instantiate_result.contract_address.to_string();
assert_eq!(48, contract_account.len(), "{contract_account:?}");

// call the contract
// the value should be true
let call = CallCommand::new()
.extrinsic_opts(opts.clone())
.message("get")
.contract(instantiate_result.contract_address.clone())
.done()
.await;
let result = call.call_dry_run().await;
assert!(result.is_ok(), "call failed");
let result = result.unwrap();
let ret_val = result.result.unwrap();
let value = call
.transcoder()
.decode_message_return(call.message(), &mut &ret_val.data[..])
.unwrap()
.to_string();
assert!(value.contains("true"), "{:#?}", value);

// call the contract
// flip the value
let call = CallCommand::new()
.extrinsic_opts(opts.clone())
.message("flip")
.contract(instantiate_result.contract_address.clone())
.done()
.await;
let call_result = call.call(None).await;
assert!(call_result.is_ok(), "call failed");
let call_result = call_result.unwrap();
let output = call_result.to_json().unwrap();
assert!(output.contains("ExtrinsicSuccess"), "{:#?}", output);

// call the contract
// make sure the value has been flipped
let call = CallCommand::new()
.extrinsic_opts(opts.clone())
.message("get")
.contract(instantiate_result.contract_address.clone())
.done()
.await;
let result = call.call_dry_run().await;
assert!(result.is_ok(), "call failed");
let result = result.unwrap();
let ret_val = result.result.unwrap();
let value = call
.transcoder()
.decode_message_return(call.message(), &mut &ret_val.data[..])
.unwrap()
.to_string();
assert!(value.contains("false"), "{:#?}", value);

// prevent the node_process from being dropped and killed
let _ = node_process;
}

/// Sanity test the whole lifecycle of:
/// build -> upload -> remove
#[tokio::test]
async fn api_build_upload_remove() {
init_tracing_subscriber();

let tmp_dir = tempfile::Builder::new()
.prefix("cargo-contract.cli.test.")
.tempdir()
.expect("temporary directory creation failed");

let node_process = ContractsNodeProcess::spawn(CONTRACTS_NODE)
.await
.expect("Error spawning contracts node");

cargo_contract(tmp_dir.path())
.arg("new")
.arg("incrementer")
.assert()
.success();

let mut project_path = tmp_dir.path().to_path_buf();
project_path.push("incrementer");

cargo_contract(project_path.as_path())
.arg("build")
.assert()
.success();

// construct the contract file path
let contract_file = project_path.join("target/ink/incrementer.contract");

// upload the contract
let opts = ExtrinsicOpts::new()
.file(contract_file)
.suri("//Alice")
.done();
let upload = UploadCommand::new()
.extrinsic_opts(opts.clone())
.done()
.await;
let upload_result = upload.upload_code().await;
assert!(upload_result.is_ok(), "upload code failed");
let upload_result = upload_result.unwrap();
let code_hash_h256 = upload_result.code_stored.unwrap().code_hash;
let code_hash = hex::encode(code_hash_h256);
assert_eq!(64, code_hash.len(), "{code_hash:?}");

// remove the contract
let remove = RemoveCommand::new()
.extrinsic_opts(opts.clone())
.code_hash(code_hash_h256)
.done()
.await;
let remove_result = remove.remove_code().await;
assert!(remove_result.is_ok(), "remove code failed");
remove_result.unwrap();

// prevent the node_process from being dropped and killed
let _ = node_process;
}

0 comments on commit 1d4348d

Please sign in to comment.