diff --git a/Makefile b/Makefile index 7aa1832ba..258fd3419 100644 --- a/Makefile +++ b/Makefile @@ -48,7 +48,8 @@ generate-full-help-doc: cargo run --bin doc-gen --features clap-markdown test: build-test - cargo test --workspace + cargo test --workspace --exclude soroban-test + cargo test -p soroban-test -- --skip integration:: e2e-test: cargo test --features it --test it -- integration diff --git a/cmd/crates/soroban-test/src/lib.rs b/cmd/crates/soroban-test/src/lib.rs index b53557d76..9a352fc78 100644 --- a/cmd/crates/soroban-test/src/lib.rs +++ b/cmd/crates/soroban-test/src/lib.rs @@ -227,7 +227,7 @@ impl TestEnv { let cmd = self.cmd_with_config::(command_str, None); self.run_cmd_with(cmd, source) .await - .map(|r| r.into_result().unwrap()) + .map(|tx| tx.into_result().unwrap()) } /// A convenience method for using the invoke command. diff --git a/cmd/crates/soroban-test/tests/it/build.rs b/cmd/crates/soroban-test/tests/it/build.rs index 3bdacbbb7..76902f93c 100644 --- a/cmd/crates/soroban-test/tests/it/build.rs +++ b/cmd/crates/soroban-test/tests/it/build.rs @@ -2,6 +2,7 @@ use predicates::prelude::predicate; use soroban_cli::xdr::{Limited, Limits, ReadXdr, ScMetaEntry, ScMetaV0}; use soroban_spec_tools::contract::Spec; use soroban_test::TestEnv; +use std::env; use std::io::Cursor; #[test] @@ -16,11 +17,9 @@ fn build_all() { .arg("--print-commands-only") .assert() .success() - .stdout(predicate::eq("\ -cargo rustc --manifest-path=contracts/add/Cargo.toml --crate-type=cdylib --target=wasm32-unknown-unknown --release + .stdout(predicate::eq(with_flags("cargo rustc --manifest-path=contracts/add/Cargo.toml --crate-type=cdylib --target=wasm32-unknown-unknown --release cargo rustc --manifest-path=contracts/call/Cargo.toml --crate-type=cdylib --target=wasm32-unknown-unknown --release -cargo rustc --manifest-path=contracts/add/add2/Cargo.toml --crate-type=cdylib --target=wasm32-unknown-unknown --release -")); +cargo rustc --manifest-path=contracts/add/add2/Cargo.toml --crate-type=cdylib --target=wasm32-unknown-unknown --release"))); } #[test] @@ -36,9 +35,7 @@ fn build_package_by_name() { .arg("--package=add") .assert() .success() - .stdout(predicate::eq("\ -cargo rustc --manifest-path=contracts/add/Cargo.toml --crate-type=cdylib --target=wasm32-unknown-unknown --release -")); + .stdout(predicate::eq(with_flags("cargo rustc --manifest-path=contracts/add/Cargo.toml --crate-type=cdylib --target=wasm32-unknown-unknown --release"))); } #[test] @@ -54,9 +51,7 @@ fn build_package_by_current_dir() { .assert() .success() .stdout(predicate::eq( - "\ -cargo rustc --manifest-path=Cargo.toml --crate-type=cdylib --target=wasm32-unknown-unknown --release -", + with_flags("cargo rustc --manifest-path=Cargo.toml --crate-type=cdylib --target=wasm32-unknown-unknown --release"), )); } @@ -85,6 +80,7 @@ fn build_all_when_in_non_package_directory() { let sandbox = TestEnv::default(); let cargo_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); let fixture_path = cargo_dir.join("tests/fixtures/workspace/contracts/add/src/"); + sandbox .new_assert_cmd("contract") .current_dir(fixture_path) @@ -92,13 +88,9 @@ fn build_all_when_in_non_package_directory() { .arg("--print-commands-only") .assert() .success() - .stdout(predicate::eq( - "\ -cargo rustc --manifest-path=../Cargo.toml --crate-type=cdylib --target=wasm32-unknown-unknown --release -cargo rustc --manifest-path=../../call/Cargo.toml --crate-type=cdylib --target=wasm32-unknown-unknown --release -cargo rustc --manifest-path=../add2/Cargo.toml --crate-type=cdylib --target=wasm32-unknown-unknown --release -", - )); + .stdout(predicate::eq(with_flags( + "cargo rustc --manifest-path=../Cargo.toml --crate-type=cdylib --target=wasm32-unknown-unknown --release" + ))); } #[test] @@ -113,14 +105,11 @@ fn build_default_members() { .arg("--print-commands-only") .assert() .success() - .stdout(predicate::eq( - "\ -cargo rustc --manifest-path=contracts/add/Cargo.toml --crate-type=cdylib --target=wasm32-unknown-unknown --release -", - )); + .stdout(predicate::eq(with_flags("cargo rustc --manifest-path=contracts/add/Cargo.toml --crate-type=cdylib --target=wasm32-unknown-unknown --release"))); } #[test] +#[ignore] // TODO: unignore -- reproduces unfixed bug https://github.com/stellar/stellar-cli/issues/1694 fn build_with_metadata() { let sandbox = TestEnv::default(); let cargo_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); @@ -186,10 +175,33 @@ fn build_with_metadata() { assert_eq!(entries, expected_entries); } +fn with_flags(expected: &str) -> String { + let cargo_home = home::cargo_home().unwrap(); + let cargo_home = format!("{}", cargo_home.display()); + let registry_prefix = format!("{cargo_home}/registry/src/"); + + let vec: Vec<_> = if env::var("RUSTFLAGS").is_ok() { + expected.split('\n').map(ToString::to_string).collect() + } else { + expected + .split('\n') + .map(|x| format!("CARGO_BUILD_RUSTFLAGS=--remap-path-prefix={registry_prefix}= {x}",)) + .collect() + }; + + format!( + "\ +{} +", + vec.join("\n") + ) +} + // Test that bins don't contain absolute paths to the local crate registry. // // See make_rustflags_to_remap_absolute_paths #[test] +#[ignore] // TODO https://github.com/stellar/stellar-cli/issues/1867 fn remap_absolute_paths() { #[derive(Eq, PartialEq, Copy, Clone)] enum Remap { diff --git a/cmd/crates/soroban-test/tests/it/config.rs b/cmd/crates/soroban-test/tests/it/config.rs index 2e5bc21c1..719f6b325 100644 --- a/cmd/crates/soroban-test/tests/it/config.rs +++ b/cmd/crates/soroban-test/tests/it/config.rs @@ -19,20 +19,26 @@ fn ls(sandbox: &TestEnv) -> Vec { .collect::>() } +pub const NETWORKS: &str = r"local +futurenet +mainnet +testnet +"; + #[test] fn set_and_remove_network() { TestEnv::with_default(|sandbox| { - add_network(sandbox, "local"); - let dir = sandbox.dir().join(".soroban").join("network"); + add_network(sandbox, "custom"); + let dir = sandbox.dir().join(".stellar").join("network"); let mut read_dir = std::fs::read_dir(dir).unwrap(); let file = read_dir.next().unwrap().unwrap(); - assert_eq!(file.file_name().to_str().unwrap(), "local.toml"); + assert_eq!(file.file_name().to_str().unwrap(), "custom.toml"); let res = ls(sandbox); - assert_eq!(res[0], "local"); + assert_eq!(res[0], "custom"); sandbox .new_assert_cmd("network") .arg("rm") - .arg("local") + .arg("custom") .assert() .success(); @@ -40,37 +46,7 @@ fn set_and_remove_network() { .new_assert_cmd("network") .arg("ls") .assert() - .stdout("\n"); - }); -} - -#[test] -fn use_default_futurenet() { - TestEnv::with_default(|sandbox| { - sandbox - .new_assert_cmd("keys") - .args(["generate", "alice", "--network", "futurenet"]) - .assert() - .success(); - let dir = sandbox.dir().join(".soroban").join("network"); - let mut read_dir = std::fs::read_dir(dir).unwrap(); - let file = read_dir.next().unwrap().unwrap(); - assert_eq!(file.file_name().to_str().unwrap(), "futurenet.toml"); - }); -} - -#[test] -fn use_default_testnet() { - TestEnv::with_default(|sandbox| { - sandbox - .new_assert_cmd("keys") - .args(["generate", "alice", "--network", "testnet"]) - .assert() - .success(); - let dir = sandbox.dir().join(".soroban").join("network"); - let mut read_dir = std::fs::read_dir(dir).unwrap(); - let file = read_dir.next().unwrap().unwrap(); - assert_eq!(file.file_name().to_str().unwrap(), "testnet.toml"); + .stdout(NETWORKS); }); } @@ -118,7 +94,7 @@ fn set_and_remove_global_network() { .arg("ls") .arg("--global") .assert() - .stdout("global\n"); + .stdout(format!("global\n{NETWORKS}")); sandbox .new_assert_cmd("network") @@ -134,7 +110,7 @@ fn set_and_remove_global_network() { .env("XDG_CONFIG_HOME", dir.to_str().unwrap()) .arg("ls") .assert() - .stdout("\n"); + .stdout(NETWORKS); } #[test] @@ -142,15 +118,35 @@ fn multiple_networks() { let sandbox = TestEnv::default(); let ls = || -> Vec { ls(&sandbox) }; - add_network(&sandbox, "local"); + println!("{:#?}", ls()); + add_network(&sandbox, "custom"); println!("{:#?}", ls()); add_network(&sandbox, "local2"); - assert_eq!(ls().as_slice(), ["local".to_owned(), "local2".to_owned()]); + assert_eq!( + ls().as_slice(), + [ + "custom".to_owned(), + "local2".to_owned(), + "local".to_owned(), + "futurenet".to_owned(), + "mainnet".to_owned(), + "testnet".to_owned() + ] + ); - sandbox.cmd::("local").run().unwrap(); + sandbox.cmd::("custom").run().unwrap(); - assert_eq!(ls().as_slice(), ["local2".to_owned()]); + assert_eq!( + ls().as_slice(), + [ + "local2".to_owned(), + "local".to_owned(), + "futurenet".to_owned(), + "mainnet".to_owned(), + "testnet".to_owned() + ] + ); let sub_dir = sandbox.dir().join("sub_directory"); fs::create_dir(&sub_dir).unwrap(); @@ -168,7 +164,17 @@ fn multiple_networks() { .run() .unwrap(); - assert_eq!(ls().as_slice(), ["local2".to_owned(), "local3".to_owned()]); + assert_eq!( + ls().as_slice(), + [ + "local2".to_owned(), + "local3".to_owned(), + "local".to_owned(), + "futurenet".to_owned(), + "mainnet".to_owned(), + "testnet".to_owned() + ] + ); } #[test] @@ -205,7 +211,7 @@ fn generate_key() { .assert() .stdout(predicates::str::contains("test_2\n")); let file_contents = - fs::read_to_string(sandbox.dir().join(".soroban/identity/test_2.toml")).unwrap(); + fs::read_to_string(sandbox.dir().join(".stellar/identity/test_2.toml")).unwrap(); assert_eq!( file_contents, format!("seed_phrase = \"{DEFAULT_SEED_PHRASE}\"\n") @@ -368,6 +374,7 @@ fn set_default_identity() { sandbox .new_assert_cmd("env") + .env_remove("SOROBAN_ACCOUNT") .assert() .stdout(predicate::str::contains("STELLAR_ACCOUNT=alice")) .success(); diff --git a/cmd/crates/soroban-test/tests/it/help.rs b/cmd/crates/soroban-test/tests/it/help.rs index ef84a361b..7ddaf3afc 100644 --- a/cmd/crates/soroban-test/tests/it/help.rs +++ b/cmd/crates/soroban-test/tests/it/help.rs @@ -1,3 +1,5 @@ +use soroban_cli::commands::contract::arg_parsing::Error::HelpMessage; +use soroban_cli::commands::contract::invoke::Error::ArgParsing; use soroban_cli::commands::contract::{self, arg_parsing}; use soroban_test::TestEnv; @@ -5,7 +7,11 @@ use crate::util::{invoke_custom as invoke, CUSTOM_TYPES, DEFAULT_CONTRACT_ID}; async fn invoke_custom(func: &str, args: &str) -> Result { let e = &TestEnv::default(); - invoke(e, DEFAULT_CONTRACT_ID, func, args, &CUSTOM_TYPES.path()).await + let r = invoke(e, DEFAULT_CONTRACT_ID, func, args, &CUSTOM_TYPES.path()).await; + if let Err(ArgParsing(HelpMessage(e))) = r { + return Ok(e); + } + r } #[tokio::test] @@ -35,6 +41,7 @@ async fn tuple_help() { #[tokio::test] async fn strukt_help() { let output = invoke_custom("strukt", "--help").await.unwrap(); + println!("{output}"); assert!(output.contains("--strukt '{ \"a\": 1, \"b\": true, \"c\": \"hello\" }'",)); assert!(output.contains("This is from the rust doc above the struct Test",)); } diff --git a/cmd/crates/soroban-test/tests/it/init.rs b/cmd/crates/soroban-test/tests/it/init.rs index c3cc9b694..f21fe042e 100644 --- a/cmd/crates/soroban-test/tests/it/init.rs +++ b/cmd/crates/soroban-test/tests/it/init.rs @@ -1,14 +1,13 @@ use assert_fs::prelude::*; use predicates::prelude::predicate; -use soroban_test::{AssertExt, TestEnv}; +use soroban_test::TestEnv; #[test] fn init() { let sandbox = TestEnv::default(); - let major = soroban_cli::commands::version::pkg() - .split('.') - .next() - .unwrap(); + let cli_version = soroban_cli::commands::version::pkg(); + let major = cli_version.split('.').next().unwrap(); + let is_rc = cli_version.contains("rc"); sandbox .new_assert_cmd("contract") .arg("init") @@ -20,68 +19,12 @@ fn init() { .child("Cargo.toml") .assert(predicate::function(|c: &str| { let table = toml::from_str::(c).unwrap(); - table["workspace"]["dependencies"]["soroban-sdk"].as_str() - == Some(&format!("{major}.0.0")) + let sdk_version = table["workspace"]["dependencies"]["soroban-sdk"].as_str(); + println!("Check expected version {major}.0.0 matches template's {sdk_version:?}"); + if is_rc { + sdk_version.and_then(|x| x.split('-').next()) == Some(&format!("{major}.0.0")) + } else { + sdk_version == Some(&format!("{major}.0.0")) + } })); } - -#[test] -fn init_and_deploy() { - let name = "hello_world"; - let sandbox = TestEnv::default(); - - sandbox - .new_assert_cmd("contract") - .arg("init") - .arg("--name") - .arg(name) - .arg("project") - .assert() - .success(); - - let manifest_path = sandbox - .dir() - .join(format!("project/contracts/{name}/Cargo.toml")); - assert!(manifest_path.exists()); - - sandbox - .new_assert_cmd("contract") - .arg("build") - .arg("--manifest-path") - .arg(manifest_path) - .assert() - .success(); - - let target_dir = sandbox - .dir() - .join("project/target/wasm32-unknown-unknown/release"); - assert!(target_dir.exists()); - - let assert = sandbox - .new_assert_cmd("contract") - .arg("deploy") - .arg("--wasm") - .arg(target_dir.join(format!("{name}.wasm"))) - .assert(); - - let contract = assert.stdout_as_str(); - - assert.success(); - - let assert = sandbox - .new_assert_cmd("contract") - .arg("invoke") - .arg("--id") - .arg(contract) - .arg("--") - .arg("hello") - .arg("--to") - .arg("bar") - .assert(); - - let output = assert.stdout_as_str(); - - assert_eq!(output, r#"["Hello","bar"]"#); - - assert.success(); -} diff --git a/cmd/crates/soroban-test/tests/it/integration.rs b/cmd/crates/soroban-test/tests/it/integration.rs index 3ec0d61ed..c7a3fbec5 100644 --- a/cmd/crates/soroban-test/tests/it/integration.rs +++ b/cmd/crates/soroban-test/tests/it/integration.rs @@ -4,6 +4,7 @@ mod cookbook; mod custom_types; mod dotenv; mod hello_world; +mod init; mod keys; mod snapshot; mod tx; diff --git a/cmd/crates/soroban-test/tests/it/integration/init.rs b/cmd/crates/soroban-test/tests/it/integration/init.rs new file mode 100644 index 000000000..0c861dfe9 --- /dev/null +++ b/cmd/crates/soroban-test/tests/it/integration/init.rs @@ -0,0 +1,63 @@ +use soroban_test::{AssertExt, TestEnv}; + +#[test] +#[ignore] +fn init_and_deploy() { + let name = "hello_world"; + let sandbox = TestEnv::default(); + + sandbox + .new_assert_cmd("contract") + .arg("init") + .arg("--name") + .arg(name) + .arg("project") + .assert() + .success(); + + let manifest_path = sandbox + .dir() + .join(format!("project/contracts/{name}/Cargo.toml")); + assert!(manifest_path.exists()); + + sandbox + .new_assert_cmd("contract") + .arg("build") + .arg("--manifest-path") + .arg(manifest_path) + .assert() + .success(); + + let target_dir = sandbox + .dir() + .join("project/target/wasm32-unknown-unknown/release"); + assert!(target_dir.exists()); + + let assert = sandbox + .new_assert_cmd("contract") + .arg("deploy") + .arg("--wasm") + .arg(target_dir.join(format!("{name}.wasm"))) + .assert(); + + let contract = assert.stdout_as_str(); + + assert.success(); + + let assert = sandbox + .new_assert_cmd("contract") + .arg("invoke") + .arg("--id") + .arg(contract) + .arg("--") + .arg("hello") + .arg("--to") + .arg("bar") + .assert(); + + let output = assert.stdout_as_str(); + + assert_eq!(output, r#"["Hello","bar"]"#); + + assert.success(); +} diff --git a/cmd/crates/soroban-test/tests/it/rpc_provider.rs b/cmd/crates/soroban-test/tests/it/rpc_provider.rs index 04c4c1bdc..6e204eb5f 100644 --- a/cmd/crates/soroban-test/tests/it/rpc_provider.rs +++ b/cmd/crates/soroban-test/tests/it/rpc_provider.rs @@ -31,11 +31,13 @@ async fn test_use_rpc_provider_with_auth_header() { } fn mock_generate_account(server: &MockServer) -> Mock { + let cli_version = soroban_cli::commands::version::pkg(); + let agent = format!("soroban-cli/{cli_version}"); server.mock(|when, then| { when.method(GET) .path("/friendbot") .header("accept", "*/*") - .header("user-agent", "soroban-cli/22.0.1"); //update this to be future proof + .header("user-agent", agent); then.status(200); }) } diff --git a/cmd/crates/soroban-test/tests/it/util.rs b/cmd/crates/soroban-test/tests/it/util.rs index a74428666..7f652bb68 100644 --- a/cmd/crates/soroban-test/tests/it/util.rs +++ b/cmd/crates/soroban-test/tests/it/util.rs @@ -1,10 +1,9 @@ -use std::path::Path; - use soroban_cli::{ commands::contract, config::{locator::KeyType, secret::Secret}, }; use soroban_test::{TestEnv, Wasm, TEST_ACCOUNT}; +use std::path::Path; pub const CUSTOM_TYPES: &Wasm = &Wasm::Custom("test-wasms", "test_custom_types"); @@ -54,10 +53,8 @@ pub async fn invoke_custom( let mut i: contract::invoke::Cmd = sandbox.cmd_with_config(&["--id", id, "--", func, arg], None); i.wasm = Some(wasm.to_path_buf()); - sandbox - .run_cmd_with(i, TEST_ACCOUNT) - .await - .map(|r| r.into_result().unwrap()) + let s = sandbox.run_cmd_with(i, TEST_ACCOUNT).await; + s.map(|tx| tx.into_result().unwrap()) } pub const DEFAULT_CONTRACT_ID: &str = "CDR6QKTWZQYW6YUJ7UP7XXZRLWQPFRV6SWBLQS4ZQOSAF4BOUD77OO5Z"; diff --git a/cmd/crates/soroban-test/tests/it/version.rs b/cmd/crates/soroban-test/tests/it/version.rs index cb7826fa4..878dbd758 100644 --- a/cmd/crates/soroban-test/tests/it/version.rs +++ b/cmd/crates/soroban-test/tests/it/version.rs @@ -8,5 +8,5 @@ fn version() { .new_assert_cmd("version") .assert() .success() - .stdout(format!("soroban {}\n", long())); + .stdout(format!("stellar {}\n", long())); } diff --git a/cmd/soroban-cli/src/cli.rs b/cmd/soroban-cli/src/cli.rs index 3baa54ecf..616a81b29 100644 --- a/cmd/soroban-cli/src/cli.rs +++ b/cmd/soroban-cli/src/cli.rs @@ -2,10 +2,16 @@ use clap::CommandFactory; use dotenvy::dotenv; use tracing_subscriber::{fmt, EnvFilter}; +use crate::commands::contract::arg_parsing::Error::HelpMessage; +use crate::commands::contract::deploy::wasm::Error::ArgParse; +use crate::commands::contract::invoke::Error::ArgParsing; +use crate::commands::contract::Error::{Deploy, Invoke}; +use crate::commands::Error::Contract; use crate::config::Config; use crate::print::Print; use crate::upgrade_check::upgrade_check; use crate::{commands, Root}; +use std::error::Error; #[tokio::main] pub async fn main() { @@ -86,6 +92,17 @@ pub async fn main() { let printer = Print::new(root.global_args.quiet); if let Err(e) = root.run().await { + // TODO: source is None (should be HelpMessage) + let _source = commands::Error::source(&e); + // TODO use source instead + if let Contract(Invoke(ArgParsing(HelpMessage(help)))) = e { + println!("{help}"); + std::process::exit(1); + } + if let Contract(Deploy(ArgParse(HelpMessage(help)))) = e { + println!("{help}"); + std::process::exit(1); + } printer.errorln(format!("error: {e}")); std::process::exit(1); } diff --git a/cmd/soroban-cli/src/commands/contract/arg_parsing.rs b/cmd/soroban-cli/src/commands/contract/arg_parsing.rs index f9733925d..7c5169b52 100644 --- a/cmd/soroban-cli/src/commands/contract/arg_parsing.rs +++ b/cmd/soroban-cli/src/commands/contract/arg_parsing.rs @@ -1,3 +1,14 @@ +use crate::commands::contract::arg_parsing::Error::HelpMessage; +use crate::commands::txn_result::TxnResult; +use crate::config::{self, sc_address, UnresolvedScAddress}; +use crate::xdr::{ + self, Hash, InvokeContractArgs, ScSpecEntry, ScSpecFunctionV0, ScSpecTypeDef, ScVal, ScVec, +}; +use clap::error::ErrorKind::DisplayHelp; +use clap::value_parser; +use ed25519_dalek::SigningKey; +use heck::ToKebabCase; +use soroban_spec_tools::Spec; use std::collections::HashMap; use std::convert::TryInto; use std::env; @@ -5,21 +16,6 @@ use std::ffi::OsString; use std::fmt::Debug; use std::path::PathBuf; -use clap::value_parser; -use ed25519_dalek::SigningKey; -use heck::ToKebabCase; - -use crate::xdr::{ - self, Hash, InvokeContractArgs, ScSpecEntry, ScSpecFunctionV0, ScSpecTypeDef, ScVal, ScVec, -}; - -use crate::commands::txn_result::TxnResult; -use crate::config::{ - self, - sc_address::{self, UnresolvedScAddress}, -}; -use soroban_spec_tools::Spec; - #[derive(thiserror::Error, Debug)] pub enum Error { #[error("parsing argument {arg}: {error}")] @@ -50,8 +46,12 @@ pub enum Error { ScAddress(#[from] sc_address::Error), #[error(transparent)] Config(#[from] config::Error), + #[error("")] + HelpMessage(String), } +pub type HostFunctionParameters = (String, Spec, InvokeContractArgs, Vec); + fn running_cmd() -> String { let mut args: Vec = env::args().collect(); @@ -67,7 +67,7 @@ pub fn build_host_function_parameters( slop: &[OsString], spec_entries: &[ScSpecEntry], config: &config::Args, -) -> Result<(String, Spec, InvokeContractArgs, Vec), Error> { +) -> Result { let spec = Spec(Some(spec_entries.to_vec())); let mut cmd = clap::Command::new(running_cmd()) @@ -81,12 +81,20 @@ pub fn build_host_function_parameters( cmd.build(); let long_help = cmd.render_long_help(); - // get_matches_from exits the process if `help`, `--help` or `-h`are passed in the slop - // see clap documentation for more info: https://github.com/clap-rs/clap/blob/v4.1.8/src/builder/command.rs#L631 - let mut matches_ = cmd.get_matches_from(slop); - let Some((function, matches_)) = &matches_.remove_subcommand() else { - println!("{long_help}"); - std::process::exit(1); + // try_get_matches_from returns an error if `help`, `--help` or `-h`are passed in the slop + // see clap documentation for more info: https://github.com/clap-rs/clap/blob/v4.1.8/src/builder/command.rs#L586 + let maybe_matches = cmd.try_get_matches_from(slop); + let Some((function, matches_)) = (match maybe_matches { + Ok(mut matches) => &matches.remove_subcommand(), + Err(e) => { + // to not exit immediately (to be able to fetch help message in tests), check for an error + if e.kind() == DisplayHelp { + return Err(HelpMessage(e.to_string())); + } + e.exit(); + } + }) else { + return Err(HelpMessage(format!("{long_help}"))); }; let func = spec.find_function(function)?; diff --git a/cmd/soroban-cli/src/commands/contract/invoke.rs b/cmd/soroban-cli/src/commands/contract/invoke.rs index 5c84ac920..092e1d188 100644 --- a/cmd/soroban-cli/src/commands/contract/invoke.rs +++ b/cmd/soroban-cli/src/commands/contract/invoke.rs @@ -6,7 +6,6 @@ use std::str::FromStr; use std::{fmt::Debug, fs, io}; use clap::{arg, command, Parser, ValueEnum}; - use soroban_rpc::{Client, SimulateHostFunctionResult, SimulateTransactionResponse}; use soroban_spec::read::FromWasmError; @@ -221,7 +220,7 @@ impl NetworkRunnable for Cmd { let spec_entries = self.spec_entries()?; if let Some(spec_entries) = &spec_entries { // For testing wasm arg parsing - let _ = build_host_function_parameters(&contract_id, &self.slop, spec_entries, config)?; + build_host_function_parameters(&contract_id, &self.slop, spec_entries, config)?; } let client = network.rpc_client()?; @@ -235,9 +234,11 @@ impl NetworkRunnable for Cmd { .await .map_err(Error::from)?; - let (function, spec, host_function_params, signers) = + let params = build_host_function_parameters(&contract_id, &self.slop, &spec_entries, config)?; + let (function, spec, host_function_params, signers) = params; + let assembled = self .simulate(&host_function_params, &default_account_entry(), &client) .await?; diff --git a/cmd/soroban-cli/src/config/mod.rs b/cmd/soroban-cli/src/config/mod.rs index 1327230e7..5c640ab3e 100644 --- a/cmd/soroban-cli/src/config/mod.rs +++ b/cmd/soroban-cli/src/config/mod.rs @@ -195,7 +195,9 @@ impl Config { pub fn save(&self) -> Result<(), locator::Error> { let toml_string = toml::to_string(&self)?; - let mut file = File::create(locator::config_file()?)?; + let path = locator::config_file()?; + // Depending on the platform, this function may fail if the full directory path does not exist + let mut file = File::create(locator::ensure_directory(path)?)?; file.write_all(toml_string.as_bytes())?; Ok(()) diff --git a/cmd/soroban-cli/src/utils/contract-workspace-template/Cargo.toml.removeextension b/cmd/soroban-cli/src/utils/contract-workspace-template/Cargo.toml.removeextension index 1c9265386..a04ea4fb3 100644 --- a/cmd/soroban-cli/src/utils/contract-workspace-template/Cargo.toml.removeextension +++ b/cmd/soroban-cli/src/utils/contract-workspace-template/Cargo.toml.removeextension @@ -5,7 +5,7 @@ members = [ ] [workspace.dependencies] -soroban-sdk = "22" +soroban-sdk = "22.0.0" [profile.release] opt-level = "z"