From 173a2e4649b0fe2f08e661ec54cb40d7b1699bc0 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Wed, 4 Oct 2023 16:14:32 -0400 Subject: [PATCH] fix: move common tests to single functions to skip setup time --- cmd/crates/soroban-test/tests/it/help.rs | 118 ++++++ .../tests/it/integration/custom_types.rs | 393 +++++++----------- .../tests/it/integration/dotenv.rs | 2 +- .../tests/it/integration/hello_world.rs | 277 +++++------- .../soroban-test/tests/it/integration/util.rs | 71 ++-- cmd/crates/soroban-test/tests/it/main.rs | 1 + cmd/crates/soroban-test/tests/it/util.rs | 24 +- .../src/commands/contract/invoke.rs | 11 +- cmd/soroban-rpc/internal/test/cli_test.go | 5 +- 9 files changed, 444 insertions(+), 458 deletions(-) create mode 100644 cmd/crates/soroban-test/tests/it/help.rs diff --git a/cmd/crates/soroban-test/tests/it/help.rs b/cmd/crates/soroban-test/tests/it/help.rs new file mode 100644 index 0000000000..53773c08a8 --- /dev/null +++ b/cmd/crates/soroban-test/tests/it/help.rs @@ -0,0 +1,118 @@ +use std::path::PathBuf; + +use soroban_test::TestEnv; + +use crate::util::{invoke_custom as invoke, CUSTOM_TYPES}; + +fn invoke_custom(e: &TestEnv, func: &str) -> assert_cmd::Command { + invoke(e, "1", func, [PathBuf::from("--wasm"), CUSTOM_TYPES.path()]) +} + +#[test] +fn generate_help() { + invoke_custom(&TestEnv::default(), "strukt_hel") + .arg("--help") + .assert() + .success() + .stdout(predicates::str::contains( + "Example contract method which takes a struct", + )); +} +#[test] +fn vec_help() { + invoke_custom(&TestEnv::default(), "vec") + .arg("--help") + .assert() + .success() + .stdout(predicates::str::contains("Array")); +} + +#[test] +fn tuple_help() { + invoke_custom(&TestEnv::default(), "tuple") + .arg("--help") + .assert() + .success() + .stdout(predicates::str::contains("Tuple")); +} + +#[test] +fn strukt_help() { + invoke_custom(&TestEnv::default(), "strukt") + .arg("--help") + .assert() + .stdout(predicates::str::contains( + "--strukt '{ \"a\": 1, \"b\": true, \"c\": \"hello\" }'", + )) + .stdout(predicates::str::contains( + "This is from the rust doc above the struct Test", + )); +} + +#[test] +fn complex_enum_help() { + invoke_custom(&TestEnv::default(), "complex") + .arg("--help") + .assert() + .stdout(predicates::str::contains( + r#"--complex '{"Struct":{ "a": 1, "b": true, "c": "hello" }}"#, + )) + .stdout(predicates::str::contains( + r#"{"Tuple":[{ "a": 1, "b": true, "c": "hello" }"#, + )) + .stdout(predicates::str::contains( + r#"{"Enum":"First"|"Second"|"Third"}"#, + )) + .stdout(predicates::str::contains( + r#"{"Asset":["GDIY6AQQ75WMD4W46EYB7O6UYMHOCGQHLAQGQTKHDX4J2DYQCHVCR4W4", "-100"]}"#, + )) + .stdout(predicates::str::contains(r#""Void"'"#)); +} + +#[test] +fn multi_arg_failure() { + invoke_custom(&TestEnv::default(), "multi_args") + .arg("--b") + .assert() + .failure() + .stderr("error: Missing argument a\n"); +} + +#[test] +fn handle_arg_larger_than_i32_failure() { + invoke_custom(&TestEnv::default(), "i32_") + .arg("--i32_") + .arg(u32::MAX.to_string()) + .assert() + .failure() + .stderr(predicates::str::contains("value is not parseable")); +} + +#[test] +fn handle_arg_larger_than_i64_failure() { + invoke_custom(&TestEnv::default(), "i64_") + .arg("--i64_") + .arg(u64::MAX.to_string()) + .assert() + .failure() + .stderr(predicates::str::contains("value is not parseable")); +} + +#[test] +fn build() { + let sandbox = TestEnv::default(); + let cargo_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); + let hello_world_contract_path = + cargo_dir.join("tests/fixtures/test-wasms/hello_world/Cargo.toml"); + sandbox + .new_assert_cmd("contract") + .arg("build") + .arg("--manifest-path") + .arg(hello_world_contract_path) + .arg("--profile") + .arg("test-wasms") + .arg("--package") + .arg("test_hello_world") + .assert() + .success(); +} diff --git a/cmd/crates/soroban-test/tests/it/integration/custom_types.rs b/cmd/crates/soroban-test/tests/it/integration/custom_types.rs index 66692fbc45..fe4ef9786b 100644 --- a/cmd/crates/soroban-test/tests/it/integration/custom_types.rs +++ b/cmd/crates/soroban-test/tests/it/integration/custom_types.rs @@ -3,11 +3,67 @@ use serde_json::json; use soroban_cli::commands; use soroban_test::TestEnv; -use super::util::{invoke, invoke_with_roundtrip, CUSTOM_TYPES}; +use crate::integration::util::{bump_contract, deploy_custom, CUSTOM_TYPES}; -#[test] -fn symbol() { - invoke(&TestEnv::default(), "hello") +use super::util::invoke_with_roundtrip; + +fn invoke_custom(e: &TestEnv, id: &str, func: &str) -> assert_cmd::Command { + let mut s = e.new_assert_cmd("contract"); + s.arg("invoke").arg("--id").arg(id).arg("--").arg(func); + s +} + +#[tokio::test] +async fn parse() { + let sandbox = &TestEnv::default(); + let id = &deploy_custom(sandbox); + bump_contract(sandbox, id, CUSTOM_TYPES).await; + symbol(sandbox, id); + string_with_quotes(sandbox, id).await; + symbol_with_quotes(sandbox, id).await; + multi_arg_success(sandbox, id); + bytes_as_file(sandbox, id); + map(sandbox, id).await; + vec_(sandbox, id).await; + tuple(sandbox, id).await; + strukt(sandbox, id).await; + tuple_strukt(sandbox, id).await; + enum_2_str(sandbox, id).await; + e_2_s_enum(sandbox, id).await; + asset(sandbox, id).await; + e_2_s_tuple(sandbox, id).await; + e_2_s_strukt(sandbox, id).await; + number_arg(sandbox, id).await; + number_arg_return_err(sandbox, id).await; + i32(sandbox, id).await; + i64(sandbox, id).await; + negative_i32(sandbox, id).await; + negative_i64(sandbox, id).await; + account_address(sandbox, id).await; + contract_address(sandbox, id).await; + bytes(sandbox, id).await; + const_enum(sandbox, id).await; + number_arg_return_ok(sandbox, id); + void(sandbox, id); + val(sandbox, id); + parse_u128(sandbox, id); + parse_i128(sandbox, id); + parse_negative_i128(sandbox, id); + parse_u256(sandbox, id); + parse_i256(sandbox, id); + parse_negative_i256(sandbox, id); + boolean(sandbox, id); + boolean_two(sandbox, id); + boolean_no_flag(sandbox, id); + boolean_false(sandbox, id); + boolean_not(sandbox, id); + boolean_not_no_flag(sandbox, id); + option_none(sandbox, id); + option_some(sandbox, id); +} + +fn symbol(sandbox: &TestEnv, id: &str) { + invoke_custom(sandbox, id, "hello") .arg("--hello") .arg("world") .assert() @@ -18,39 +74,16 @@ fn symbol() { ); } -#[tokio::test] -async fn string_with_quotes() { - invoke_with_roundtrip("string", json!("hello world")).await; -} - -#[tokio::test] -async fn symbol_with_quotes() { - invoke_with_roundtrip("hello", json!("world")).await; -} - -#[test] -fn generate_help() { - invoke(&TestEnv::default(), "strukt_hel") - .arg("--help") - .assert() - .success() - .stdout(predicates::str::contains( - "Example contract method which takes a struct", - )); +async fn string_with_quotes(sandbox: &TestEnv, id: &str) { + invoke_with_roundtrip(sandbox, id, "string", json!("hello world")).await; } -#[test] -fn multi_arg_failure() { - invoke(&TestEnv::default(), "multi_args") - .arg("--b") - .assert() - .failure() - .stderr("error: Missing argument a\n"); +async fn symbol_with_quotes(sandbox: &TestEnv, id: &str) { + invoke_with_roundtrip(sandbox, id, "hello", json!("world")).await; } -#[test] -fn multi_arg_success() { - invoke(&TestEnv::default(), "multi_args") +fn multi_arg_success(sandbox: &TestEnv, id: &str) { + invoke_custom(sandbox, id, "multi_args") .arg("--a") .arg("42") .arg("--b") @@ -59,12 +92,11 @@ fn multi_arg_success() { .stdout("42\n"); } -#[test] -fn bytes_as_file() { +fn bytes_as_file(sandbox: &TestEnv, id: &str) { let env = &TestEnv::default(); let path = env.temp_dir.join("bytes.txt"); std::fs::write(&path, 0x0073_7465_6c6c_6172u128.to_be_bytes()).unwrap(); - invoke(env, "bytes") + invoke_custom(sandbox, id, "bytes") .arg("--bytes-file-path") .arg(path) .assert() @@ -72,108 +104,50 @@ fn bytes_as_file() { .stdout("\"0000000000000000007374656c6c6172\"\n"); } -#[tokio::test] -async fn map() { - invoke_with_roundtrip("map", json!({"0": true, "1": false})).await; -} - -#[test] -fn map_help() { - invoke(&TestEnv::default(), "map") - .arg("--help") - .assert() - .success() - .stdout(predicates::str::contains("Map")); -} - -#[tokio::test] -async fn vec_() { - invoke_with_roundtrip("vec", json!([0, 1])).await; -} - -#[test] -fn vec_help() { - invoke(&TestEnv::default(), "vec") - .arg("--help") - .assert() - .success() - .stdout(predicates::str::contains("Array")); +async fn map(sandbox: &TestEnv, id: &str) { + invoke_with_roundtrip(sandbox, id, "map", json!({"0": true, "1": false})).await; } -#[tokio::test] -async fn tuple() { - invoke_with_roundtrip("tuple", json!(["hello", 0])).await; +async fn vec_(sandbox: &TestEnv, id: &str) { + invoke_with_roundtrip(sandbox, id, "vec", json!([0, 1])).await; } -#[tokio::test] -async fn tuple_help() { - invoke(&TestEnv::default(), "tuple") - .arg("--help") - .assert() - .success() - .stdout(predicates::str::contains("Tuple")); +async fn tuple(sandbox: &TestEnv, id: &str) { + invoke_with_roundtrip(sandbox, id, "tuple", json!(["hello", 0])).await; } -#[tokio::test] -async fn strukt() { - invoke_with_roundtrip("strukt", json!({"a": 42, "b": true, "c": "world"})).await; +async fn strukt(sandbox: &TestEnv, id: &str) { + invoke_with_roundtrip( + sandbox, + id, + "strukt", + json!({"a": 42, "b": true, "c": "world"}), + ) + .await; } -#[tokio::test] -async fn tuple_strukt() { +async fn tuple_strukt(sandbox: &TestEnv, id: &str) { invoke_with_roundtrip( + sandbox, + id, "tuple_strukt", json!([{"a": 42, "b": true, "c": "world"}, "First"]), ) .await; } -#[test] -fn strukt_help() { - invoke(&TestEnv::default(), "strukt") - .arg("--help") - .assert() - .stdout(predicates::str::contains( - "--strukt '{ \"a\": 1, \"b\": true, \"c\": \"hello\" }'", - )) - .stdout(predicates::str::contains( - "This is from the rust doc above the struct Test", - )); -} - -#[test] -fn complex_enum_help() { - invoke(&TestEnv::default(), "complex") - .arg("--help") - .assert() - .stdout(predicates::str::contains( - r#"--complex '{"Struct":{ "a": 1, "b": true, "c": "hello" }}"#, - )) - .stdout(predicates::str::contains( - r#"{"Tuple":[{ "a": 1, "b": true, "c": "hello" }"#, - )) - .stdout(predicates::str::contains( - r#"{"Enum":"First"|"Second"|"Third"}"#, - )) - .stdout(predicates::str::contains( - r#"{"Asset":["GDIY6AQQ75WMD4W46EYB7O6UYMHOCGQHLAQGQTKHDX4J2DYQCHVCR4W4", "-100"]}"#, - )) - .stdout(predicates::str::contains(r#""Void"'"#)); -} - -#[tokio::test] -async fn enum_2_str() { - invoke_with_roundtrip("simple", json!("First")).await; +async fn enum_2_str(sandbox: &TestEnv, id: &str) { + invoke_with_roundtrip(sandbox, id, "simple", json!("First")).await; } -#[tokio::test] -async fn e_2_s_enum() { - invoke_with_roundtrip("complex", json!({"Enum": "First"})).await; +async fn e_2_s_enum(sandbox: &TestEnv, id: &str) { + invoke_with_roundtrip(sandbox, id, "complex", json!({"Enum": "First"})).await; } -#[tokio::test] -async fn asset() { +async fn asset(sandbox: &TestEnv, id: &str) { invoke_with_roundtrip( + sandbox, + id, "complex", json!({"Asset": ["CB64D3G7SM2RTH6JSGG34DDTFTQ5CFDKVDZJZSODMCX4NJ2HV2KN7OHT", "100" ]}), ) @@ -184,28 +158,26 @@ fn complex_tuple() -> serde_json::Value { json!({"Tuple": [{"a": 42, "b": true, "c": "world"}, "First"]}) } -#[tokio::test] -async fn e_2_s_tuple() { - invoke_with_roundtrip("complex", complex_tuple()).await; +async fn e_2_s_tuple(sandbox: &TestEnv, id: &str) { + invoke_with_roundtrip(sandbox, id, "complex", complex_tuple()).await; } -#[tokio::test] -async fn e_2_s_strukt() { +async fn e_2_s_strukt(sandbox: &TestEnv, id: &str) { invoke_with_roundtrip( + sandbox, + id, "complex", json!({"Struct": {"a": 42, "b": true, "c": "world"}}), ) .await; } -#[tokio::test] -async fn number_arg() { - invoke_with_roundtrip("u32_", 42).await; +async fn number_arg(sandbox: &TestEnv, id: &str) { + invoke_with_roundtrip(sandbox, id, "u32_", 42).await; } -#[test] -fn number_arg_return_ok() { - invoke(&TestEnv::default(), "u32_fail_on_even") +fn number_arg_return_ok(sandbox: &TestEnv, id: &str) { + invoke_custom(sandbox, id, "u32_fail_on_even") .arg("--u32_") .arg("1") .assert() @@ -213,21 +185,9 @@ fn number_arg_return_ok() { .stdout("1\n"); } -#[tokio::test] -async fn number_arg_return_err() { - let sandbox = &TestEnv::default(); - - let p = CUSTOM_TYPES.path(); - let wasm = p.to_str().unwrap(); +async fn number_arg_return_err(sandbox: &TestEnv, id: &str) { let res = sandbox - .invoke(&[ - "--id=1", - "--wasm", - wasm, - "--", - "u32_fail_on_even", - "--u32_=2", - ]) + .invoke(&["--id", id, "--", "u32_fail_on_even", "--u32_=2"]) .await .unwrap_err(); if let commands::contract::invoke::Error::ContractInvoke(name, doc) = &res { @@ -237,96 +197,69 @@ async fn number_arg_return_err() { println!("{res:#?}"); } -#[test] -fn void() { - invoke(&TestEnv::default(), "woid") +fn void(sandbox: &TestEnv, id: &str) { + invoke_custom(sandbox, id, "woid") .assert() .success() .stdout("\n") .stderr(""); } -#[test] -fn val() { - invoke(&TestEnv::default(), "val") +fn val(sandbox: &TestEnv, id: &str) { + invoke_custom(sandbox, id, "val") .assert() .success() .stdout("null\n") .stderr(""); } -#[tokio::test] -async fn i32() { - invoke_with_roundtrip("i32_", 42).await; -} - -#[test] -fn handle_arg_larger_than_i32_failure() { - invoke(&TestEnv::default(), "i32_") - .arg("--i32_") - .arg(u32::MAX.to_string()) - .assert() - .failure() - .stderr(predicates::str::contains("value is not parseable")); +async fn i32(sandbox: &TestEnv, id: &str) { + invoke_with_roundtrip(sandbox, id, "i32_", 42).await; } -#[test] -fn handle_arg_larger_than_i64_failure() { - invoke(&TestEnv::default(), "i64_") - .arg("--i64_") - .arg(u64::MAX.to_string()) - .assert() - .failure() - .stderr(predicates::str::contains("value is not parseable")); +async fn i64(sandbox: &TestEnv, id: &str) { + invoke_with_roundtrip(sandbox, id, "i64_", i64::MAX).await; } -#[tokio::test] -async fn i64() { - invoke_with_roundtrip("i64_", i64::MAX).await; +async fn negative_i32(sandbox: &TestEnv, id: &str) { + invoke_with_roundtrip(sandbox, id, "i32_", -42).await; } -#[tokio::test] -async fn negative_i32() { - invoke_with_roundtrip("i32_", -42).await; -} - -#[tokio::test] -async fn negative_i64() { - invoke_with_roundtrip("i64_", i64::MIN).await; +async fn negative_i64(sandbox: &TestEnv, id: &str) { + invoke_with_roundtrip(sandbox, id, "i64_", i64::MIN).await; } -#[tokio::test] -async fn account_address() { +async fn account_address(sandbox: &TestEnv, id: &str) { invoke_with_roundtrip( + sandbox, + id, "addresse", json!("GD5KD2KEZJIGTC63IGW6UMUSMVUVG5IHG64HUTFWCHVZH2N2IBOQN7PS"), ) .await; } -#[tokio::test] -async fn contract_address() { +async fn contract_address(sandbox: &TestEnv, id: &str) { invoke_with_roundtrip( + sandbox, + id, "addresse", json!("CA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQGAXE"), ) .await; } -#[tokio::test] -async fn bytes() { - invoke_with_roundtrip("bytes", json!("7374656c6c6172")).await; +async fn bytes(sandbox: &TestEnv, id: &str) { + invoke_with_roundtrip(sandbox, id, "bytes", json!("7374656c6c6172")).await; } -#[tokio::test] -async fn const_enum() { - invoke_with_roundtrip("card", "11").await; +async fn const_enum(sandbox: &TestEnv, id: &str) { + invoke_with_roundtrip(sandbox, id, "card", "11").await; } -#[test] -fn parse_u128() { +fn parse_u128(sandbox: &TestEnv, id: &str) { let num = "340000000000000000000000000000000000000"; - invoke(&TestEnv::default(), "u128") + invoke_custom(sandbox, id, "u128") .arg("--u128") .arg(num) .assert() @@ -337,10 +270,9 @@ fn parse_u128() { )); } -#[test] -fn parse_i128() { +fn parse_i128(sandbox: &TestEnv, id: &str) { let num = "170000000000000000000000000000000000000"; - invoke(&TestEnv::default(), "i128") + invoke_custom(sandbox, id, "i128") .arg("--i128") .arg(num) .assert() @@ -351,10 +283,9 @@ fn parse_i128() { )); } -#[test] -fn parse_negative_i128() { +fn parse_negative_i128(sandbox: &TestEnv, id: &str) { let num = "-170000000000000000000000000000000000000"; - invoke(&TestEnv::default(), "i128") + invoke_custom(sandbox, id, "i128") .arg("--i128") .arg(num) .assert() @@ -365,10 +296,9 @@ fn parse_negative_i128() { )); } -#[test] -fn parse_u256() { +fn parse_u256(sandbox: &TestEnv, id: &str) { let num = "340000000000000000000000000000000000000"; - invoke(&TestEnv::default(), "u256") + invoke_custom(sandbox, id, "u256") .arg("--u256") .arg(num) .assert() @@ -379,10 +309,9 @@ fn parse_u256() { )); } -#[test] -fn parse_i256() { +fn parse_i256(sandbox: &TestEnv, id: &str) { let num = "170000000000000000000000000000000000000"; - invoke(&TestEnv::default(), "i256") + invoke_custom(sandbox, id, "i256") .arg("--i256") .arg(num) .assert() @@ -393,10 +322,9 @@ fn parse_i256() { )); } -#[test] -fn parse_negative_i256() { +fn parse_negative_i256(sandbox: &TestEnv, id: &str) { let num = "-170000000000000000000000000000000000000"; - invoke(&TestEnv::default(), "i256") + invoke_custom(sandbox, id, "i256") .arg("--i256") .arg(num) .assert() @@ -407,9 +335,8 @@ fn parse_negative_i256() { )); } -#[test] -fn boolean() { - invoke(&TestEnv::default(), "boolean") +fn boolean(sandbox: &TestEnv, id: &str) { + invoke_custom(sandbox, id, "boolean") .arg("--boolean") .assert() .success() @@ -418,9 +345,8 @@ fn boolean() { "#, ); } -#[test] -fn boolean_two() { - invoke(&TestEnv::default(), "boolean") +fn boolean_two(sandbox: &TestEnv, id: &str) { + invoke_custom(sandbox, id, "boolean") .arg("--boolean") .arg("true") .assert() @@ -431,9 +357,8 @@ fn boolean_two() { ); } -#[test] -fn boolean_no_flag() { - invoke(&TestEnv::default(), "boolean") +fn boolean_no_flag(sandbox: &TestEnv, id: &str) { + invoke_custom(sandbox, id, "boolean") .assert() .success() .stdout( @@ -442,9 +367,8 @@ fn boolean_no_flag() { ); } -#[test] -fn boolean_false() { - invoke(&TestEnv::default(), "boolean") +fn boolean_false(sandbox: &TestEnv, id: &str) { + invoke_custom(sandbox, id, "boolean") .arg("--boolean") .arg("false") .assert() @@ -455,9 +379,8 @@ fn boolean_false() { ); } -#[test] -fn boolean_not() { - invoke(&TestEnv::default(), "not") +fn boolean_not(sandbox: &TestEnv, id: &str) { + invoke_custom(sandbox, id, "not") .arg("--boolean") .assert() .success() @@ -467,20 +390,15 @@ fn boolean_not() { ); } -#[test] -fn boolean_not_no_flag() { - invoke(&TestEnv::default(), "not") - .assert() - .success() - .stdout( - r#"true +fn boolean_not_no_flag(sandbox: &TestEnv, id: &str) { + invoke_custom(sandbox, id, "not").assert().success().stdout( + r#"true "#, - ); + ); } -#[test] -fn option_none() { - invoke(&TestEnv::default(), "option") +fn option_none(sandbox: &TestEnv, id: &str) { + invoke_custom(sandbox, id, "option") .assert() .success() .stdout( @@ -489,9 +407,8 @@ fn option_none() { ); } -#[test] -fn option_some() { - invoke(&TestEnv::default(), "option") +fn option_some(sandbox: &TestEnv, id: &str) { + invoke_custom(sandbox, id, "option") .arg("--option=1") .assert() .success() diff --git a/cmd/crates/soroban-test/tests/it/integration/dotenv.rs b/cmd/crates/soroban-test/tests/it/integration/dotenv.rs index 5d7aa102e3..d7d56aaf3b 100644 --- a/cmd/crates/soroban-test/tests/it/integration/dotenv.rs +++ b/cmd/crates/soroban-test/tests/it/integration/dotenv.rs @@ -41,7 +41,7 @@ fn current_env_not_overwritten() { .arg("hello") .arg("--world=world") .assert() - .stderr("error: parsing contract spec: contract spec not found\n"); + .stderr("error: Contract not found: CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4\n"); }); } diff --git a/cmd/crates/soroban-test/tests/it/integration/hello_world.rs b/cmd/crates/soroban-test/tests/it/integration/hello_world.rs index 01b548e21a..1b77da2201 100644 --- a/cmd/crates/soroban-test/tests/it/integration/hello_world.rs +++ b/cmd/crates/soroban-test/tests/it/integration/hello_world.rs @@ -4,24 +4,38 @@ use soroban_cli::commands::{ }; use soroban_test::TestEnv; -use crate::util::DEFAULT_SEED_PHRASE; +use crate::{integration::util::bump_contract, util::DEFAULT_SEED_PHRASE}; use super::util::{ - add_test_seed, deploy_hello, network_passphrase, network_passphrase_arg, rpc_url, rpc_url_arg, - DEFAULT_PUB_KEY, DEFAULT_PUB_KEY_1, DEFAULT_SECRET_KEY, HELLO_WORLD, TEST_CONTRACT_ID, + add_test_seed, bump, deploy_hello, network_passphrase, network_passphrase_arg, rpc_url, + rpc_url_arg, DEFAULT_PUB_KEY, DEFAULT_PUB_KEY_1, DEFAULT_SECRET_KEY, HELLO_WORLD, }; -#[test] -fn install_wasm_then_deploy_contract() { - let sandbox = TestEnv::default(); - assert_eq!(deploy_hello(&sandbox), TEST_CONTRACT_ID); +#[tokio::test] +#[ignore] +async fn invoke() { + let sandbox = &TestEnv::default(); + let id = &deploy_hello(sandbox); + bump_contract(sandbox, id, HELLO_WORLD).await; + // Note that all functions tested here have no state + invoke_hello_world(sandbox, id); + invoke_hello_world_with_lib(sandbox, id).await; + invoke_hello_world_with_lib_two(sandbox, id).await; + invoke_auth(sandbox, id); + invoke_auth_with_identity(sandbox, id).await; + invoke_auth_with_different_test_account_fail(sandbox, id).await; + // invoke_auth_with_different_test_account(sandbox, id); + contract_data_read_failure(sandbox, id); + invoke_with_seed(sandbox, id).await; + invoke_with_sk(sandbox, id).await; + // This does add an identity to local config + invoke_with_id(sandbox, id).await; + handles_kebab_case(sandbox, id).await; + fetch(sandbox, id).await; + invoke_prng_u64_in_range_test(sandbox, id).await; } -#[test] -fn invoke_hello_world() { - let sandbox = TestEnv::default(); - let id = deploy_hello(&sandbox); - println!("{id}"); +fn invoke_hello_world(sandbox: &TestEnv, id: &str) { sandbox .new_assert_cmd("contract") .arg("invoke") @@ -35,12 +49,9 @@ fn invoke_hello_world() { .success(); } -#[tokio::test] -async fn invoke_hello_world_with_lib() { - let e = &TestEnv::default(); - let id = deploy_hello(e); +async fn invoke_hello_world_with_lib(e: &TestEnv, id: &str) { let mut cmd = contract::invoke::Cmd { - contract_id: id, + contract_id: id.to_string(), slop: vec!["hello".into(), "--world=world".into()], ..Default::default() }; @@ -52,12 +63,9 @@ async fn invoke_hello_world_with_lib() { assert_eq!(res, r#"["Hello","world"]"#); } -#[tokio::test] -async fn invoke_hello_world_with_lib_two() { - let e = &TestEnv::default(); - let id = deploy_hello(e); +async fn invoke_hello_world_with_lib_two(e: &TestEnv, id: &str) { let hello_world = HELLO_WORLD.to_string(); - let mut invoke_args = vec!["--id", &id, "--wasm", hello_world.as_str()]; + let mut invoke_args = vec!["--id", id, "--wasm", hello_world.as_str()]; let args = vec!["--", "hello", "--world=world"]; let res = if let (Some(rpc), Some(network_passphrase)) = (rpc_url_arg(), network_passphrase_arg()) { @@ -70,10 +78,7 @@ async fn invoke_hello_world_with_lib_two() { assert_eq!(res, r#"["Hello","world"]"#); } -#[test] -fn invoke_auth() { - let sandbox = TestEnv::default(); - let id = &deploy_hello(&sandbox); +fn invoke_auth(sandbox: &TestEnv, id: &str) { sandbox .new_assert_cmd("contract") .arg("invoke") @@ -104,15 +109,12 @@ fn invoke_auth() { .success(); } -#[tokio::test] -async fn invoke_auth_with_identity() { - let sandbox = TestEnv::default(); +async fn invoke_auth_with_identity(sandbox: &TestEnv, id: &str) { sandbox .cmd::("test -d ") .run() .await .unwrap(); - let id = deploy_hello(&sandbox); sandbox .new_assert_cmd("contract") .arg("invoke") @@ -122,65 +124,54 @@ async fn invoke_auth_with_identity() { .arg(HELLO_WORLD.path()) .arg("--") .arg("auth") - .arg("--addr=test") + .arg("--addr") + .arg(DEFAULT_PUB_KEY) .arg("--world=world") .assert() .stdout(format!("\"{DEFAULT_PUB_KEY}\"\n")) .success(); } -#[test] -fn invoke_auth_with_different_test_account() { - let sandbox = TestEnv::default(); - let id = deploy_hello(&sandbox); - sandbox - .new_assert_cmd("contract") - .arg("invoke") - .arg("--hd-path=1") - .arg("--id") - .arg(id) - .arg("--wasm") - .arg(HELLO_WORLD.path()) - .arg("--") - .arg("auth") - .arg(&format!("--addr={DEFAULT_PUB_KEY_1}")) - .arg("--world=world") - .assert() - .stdout(format!("\"{DEFAULT_PUB_KEY_1}\"\n")) - .success(); -} +// fn invoke_auth_with_different_test_account(sandbox: &TestEnv, id: &str) { +// sandbox +// .new_assert_cmd("contract") +// .arg("invoke") +// .arg("--hd-path=1") +// .arg("--id") +// .arg(id) +// .arg("--wasm") +// .arg(HELLO_WORLD.path()) +// .arg("--") +// .arg("auth") +// .arg(&format!("--addr={DEFAULT_PUB_KEY_1}")) +// .arg("--world=world") +// .assert() +// .stdout(format!("\"{DEFAULT_PUB_KEY_1}\"\n")) +// .success(); +// } -#[tokio::test] -async fn invoke_auth_with_different_test_account_fail() { - let sandbox = TestEnv::default(); - let id = &deploy_hello(&sandbox); +async fn invoke_auth_with_different_test_account_fail(sandbox: &TestEnv, id: &str) { let res = sandbox .invoke(&[ - "--hd-path=1", + "--hd-path=0", "--id", id, - "--wasm", - HELLO_WORLD.path().to_str().unwrap(), &rpc_url_arg().unwrap_or_default(), &network_passphrase_arg().unwrap_or_default(), "--", "auth", - &format!("--addr={DEFAULT_PUB_KEY}"), + &format!("--addr={DEFAULT_PUB_KEY_1}"), "--world=world", ]) .await; let e = res.unwrap_err(); assert!( - matches!(e, contract::invoke::Error::Host(_)), - "Expected host error got {e:?}" + matches!(e, contract::invoke::Error::Rpc(_)), + "Expected rpc error got {e:?}" ); } -#[test] -fn contract_data_read_failure() { - let sandbox = TestEnv::default(); - let id = deploy_hello(&sandbox); - +fn contract_data_read_failure(sandbox: &TestEnv, id: &str) { sandbox .new_assert_cmd("contract") .arg("read") @@ -195,27 +186,22 @@ fn contract_data_read_failure() { ); } -#[test] -fn contract_data_read() { - let sandbox = TestEnv::default(); - let id = &deploy_hello(&sandbox); - - sandbox - .new_assert_cmd("contract") - .arg("invoke") - .arg("--id") - .arg(id) - .arg("--") - .arg("inc") - .assert() - .success(); +#[tokio::test] +async fn contract_data_read() { + const KEY: &str = "COUNTER"; + let sandbox = &TestEnv::default(); + let id = &deploy_hello(sandbox); + let res = sandbox.invoke(&["--id", id, "--", "inc"]).await.unwrap(); + assert_eq!(res.trim(), "1"); + bump(sandbox, id, Some(KEY)).await; sandbox .new_assert_cmd("contract") .arg("read") .arg("--id") .arg(id) - .arg("--key=COUNTER") + .arg("--key") + .arg(KEY) .arg("--durability=persistent") .assert() .success() @@ -236,51 +222,34 @@ fn contract_data_read() { .arg("read") .arg("--id") .arg(id) - .arg("--key=COUNTER") + .arg("--key") + .arg(KEY) .arg("--durability=persistent") .assert() .success() .stdout(predicates::str::starts_with("COUNTER,2")); } -#[tokio::test] -async fn invoke_hello_world_with_seed() { - let sandbox = TestEnv::default(); - let identity = add_test_seed(sandbox.dir()); - invoke_with_source(&sandbox, &identity).await; +async fn invoke_with_seed(sandbox: &TestEnv, id: &str) { + invoke_with_source(sandbox, DEFAULT_SEED_PHRASE, id).await; } -#[tokio::test] -async fn invoke_with_seed() { - let sandbox = TestEnv::default(); - invoke_with_source(&sandbox, DEFAULT_SEED_PHRASE).await; +async fn invoke_with_sk(sandbox: &TestEnv, id: &str) { + invoke_with_source(sandbox, DEFAULT_SECRET_KEY, id).await; } -#[tokio::test] -async fn invoke_with_id() { - let sandbox = TestEnv::default(); +async fn invoke_with_id(sandbox: &TestEnv, id: &str) { let identity = add_test_seed(sandbox.dir()); - invoke_with_source(&sandbox, &identity).await; -} - -#[tokio::test] -async fn invoke_with_sk() { - let sandbox = TestEnv::default(); - invoke_with_source(&sandbox, DEFAULT_SECRET_KEY).await; + invoke_with_source(sandbox, &identity, id).await; } -async fn invoke_with_source(sandbox: &TestEnv, source: &str) { - let id = &deploy_hello(sandbox); +async fn invoke_with_source(sandbox: &TestEnv, source: &str, id: &str) { let cmd = sandbox .invoke(&[ "--source-account", source, "--id", id, - "--wasm", - HELLO_WORLD.path().to_str().unwrap(), - &rpc_url_arg().unwrap_or_default(), - &network_passphrase_arg().unwrap_or_default(), "--", "hello", "--world=world", @@ -288,92 +257,34 @@ async fn invoke_with_source(sandbox: &TestEnv, source: &str) { .await .unwrap(); assert_eq!(cmd, "[\"Hello\",\"world\"]"); +} - // Invoke it again without providing the contract, to exercise the deployment - let cmd = sandbox - .invoke(&[ - "--source-account", - source, - "--id", - id, - "--", - "hello", - "--world=world", - ]) +async fn handles_kebab_case(e: &TestEnv, id: &str) { + assert!(e + .invoke(&["--id", id, "--", "multi-word-cmd", "--contract-owner=world",]) .await - .unwrap(); - assert_eq!(cmd, "[\"Hello\",\"world\"]"); + .is_ok()); } -#[tokio::test] -async fn handles_kebab_case() { - let e = TestEnv::default(); - let id = deploy_hello(&e); - assert!(e +async fn fetch(sandbox: &TestEnv, id: &str) { + let f = sandbox.dir().join("contract.wasm"); + let cmd = sandbox.cmd_arr::(&["--id", id, "--out-file", f.to_str().unwrap()]); + cmd.run().await.unwrap(); + assert!(f.exists()); +} + +async fn invoke_prng_u64_in_range_test(sandbox: &TestEnv, id: &str) { + assert!(sandbox .invoke(&[ "--id", - &id, + id, "--wasm", HELLO_WORLD.path().to_str().unwrap(), "--", - "multi-word-cmd", - "--contract-owner=world", + "prng_u64_in_range", + "--low=0", + "--high=100", ]) .await .is_ok()); } - -#[tokio::test] -async fn fetch() { - let e = TestEnv::default(); - let f = e.dir().join("contract.wasm"); - let id = deploy_hello(&e); - let cmd = e.cmd_arr::(&["--id", &id, "--out-file", f.to_str().unwrap()]); - cmd.run().await.unwrap(); - assert!(f.exists()); -} - -#[test] -fn build() { - let sandbox = TestEnv::default(); - let cargo_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); - let hello_world_contract_path = - cargo_dir.join("tests/fixtures/test-wasms/hello_world/Cargo.toml"); - sandbox - .new_assert_cmd("contract") - .arg("build") - .arg("--manifest-path") - .arg(hello_world_contract_path) - .arg("--profile") - .arg("test-wasms") - .arg("--package") - .arg("test_hello_world") - .assert() - .success(); -} - -#[test] -fn invoke_prng_u64_in_range_test() { - let sandbox = TestEnv::default(); - let res = sandbox - .new_assert_cmd("contract") - .arg("deploy") - .arg("--wasm") - .arg(HELLO_WORLD.path()) - .assert() - .success(); - let stdout = String::from_utf8(res.get_output().stdout.clone()).unwrap(); - let id = stdout.trim_end(); - println!("{id}"); - sandbox - .new_assert_cmd("contract") - .arg("invoke") - .arg("--id") - .arg(id) - .arg("--") - .arg("prng_u64_in_range") - .arg("--low=0") - .arg("--high=100") - .assert() - .success(); -} diff --git a/cmd/crates/soroban-test/tests/it/integration/util.rs b/cmd/crates/soroban-test/tests/it/integration/util.rs index f228a09aa9..8edc144738 100644 --- a/cmd/crates/soroban-test/tests/it/integration/util.rs +++ b/cmd/crates/soroban-test/tests/it/integration/util.rs @@ -1,7 +1,6 @@ -use std::{fmt::Display, path::Path}; - -use assert_cmd::Command; +use soroban_cli::commands::contract; use soroban_test::{TestEnv, Wasm}; +use std::{fmt::Display, path::Path}; use crate::util::{add_identity, SecretKind}; @@ -19,34 +18,14 @@ pub fn add_test_seed(dir: &Path) -> String { name.to_owned() } -pub fn invoke(sandbox: &TestEnv, func: &str) -> Command { - let mut s = sandbox.new_assert_cmd("contract"); - s.arg("invoke") - .arg("--id=1") - .arg("--wasm") - .arg(CUSTOM_TYPES.path()) - .arg("--") - .arg(func); - s -} - -pub async fn invoke_with_roundtrip(func: &str, data: D) +pub async fn invoke_with_roundtrip(e: &TestEnv, id: &str, func: &str, data: D) where D: Display, { - let e = TestEnv::default(); let data = data.to_string(); println!("{data}"); let res = e - .invoke(&[ - "--id=1", - "--wasm", - &CUSTOM_TYPES.to_string(), - "--", - func, - &format!("--{func}"), - &data, - ]) + .invoke(&["--id", id, "--", func, &format!("--{func}"), &data]) .await .unwrap(); assert_eq!(res, data); @@ -76,12 +55,20 @@ pub fn network_passphrase_arg() -> Option { } pub fn deploy_hello(sandbox: &TestEnv) -> String { - let hash = HELLO_WORLD.hash().unwrap(); + deploy_contract(sandbox, HELLO_WORLD) +} + +pub fn deploy_custom(sandbox: &TestEnv) -> String { + deploy_contract(sandbox, CUSTOM_TYPES) +} + +pub fn deploy_contract(sandbox: &TestEnv, wasm: &Wasm) -> String { + let hash = wasm.hash().unwrap(); sandbox .new_assert_cmd("contract") .arg("install") .arg("--wasm") - .arg(HELLO_WORLD.path()) + .arg(wasm.path()) .assert() .success() .stdout(format!("{hash}\n")); @@ -98,3 +85,33 @@ pub fn deploy_hello(sandbox: &TestEnv) -> String { .stdout(format!("{TEST_CONTRACT_ID}\n")); TEST_CONTRACT_ID.to_string() } + +pub async fn bump_contract(sandbox: &TestEnv, id: &str, wasm: &Wasm<'_>) { + bump(sandbox, id, None).await; + let cmd: contract::bump::Cmd = sandbox.cmd_arr(&[ + "--wasm-hash", + wasm.hash().unwrap().to_string().as_str(), + "--durability", + "persistent", + "--ledgers-to-expire", + "100000", + ]); + cmd.run().await.unwrap(); +} + +pub async fn bump(sandbox: &TestEnv, id: &str, value: Option<&str>) { + let mut args = vec![ + "--id", + id, + "--durability", + "persistent", + "--ledgers-to-expire", + "100000", + ]; + if let Some(value) = value{ + args.push("--key"); + args.push(value); + } + let cmd: contract::bump::Cmd = sandbox.cmd_arr(&args); + cmd.run().await.unwrap(); +} diff --git a/cmd/crates/soroban-test/tests/it/main.rs b/cmd/crates/soroban-test/tests/it/main.rs index 2e9d8bc5cd..3077d2fa01 100644 --- a/cmd/crates/soroban-test/tests/it/main.rs +++ b/cmd/crates/soroban-test/tests/it/main.rs @@ -1,5 +1,6 @@ mod arg_parsing; mod config; +mod help; #[cfg(feature = "integration")] mod integration; mod lab; diff --git a/cmd/crates/soroban-test/tests/it/util.rs b/cmd/crates/soroban-test/tests/it/util.rs index 75389c8a4b..3225093c4f 100644 --- a/cmd/crates/soroban-test/tests/it/util.rs +++ b/cmd/crates/soroban-test/tests/it/util.rs @@ -1,7 +1,8 @@ use std::path::Path; +use assert_cmd::Command; use soroban_cli::commands::config::{locator::KeyType, secret::Secret}; -use soroban_test::Wasm; +use soroban_test::{TestEnv, Wasm}; pub const CUSTOM_TYPES: &Wasm = &Wasm::Custom("test-wasms", "test_custom_types"); @@ -40,3 +41,24 @@ pub fn add_test_id(dir: &Path) -> String { pub const DEFAULT_SEED_PHRASE: &str = "coral light army gather adapt blossom school alcohol coral light army giggle"; + +#[allow(dead_code)] +pub fn invoke_custom(sandbox: &TestEnv, id: &str, func: &str, args: I) -> Command +where + I: IntoIterator, + S: AsRef, +{ + let mut s = sandbox.new_assert_cmd("contract"); + s.env("SOROBAN_RPC_URL", "http://localhost:8000") + .env( + "SOROBAN_NETWORK_PASSPHRASE", + "Standalone Network ; February 2017", + ) + .arg("invoke") + .arg("--id") + .arg(id) + .args(args) + .arg("--") + .arg(func); + s +} diff --git a/cmd/soroban-cli/src/commands/contract/invoke.rs b/cmd/soroban-cli/src/commands/contract/invoke.rs index d84e72fed6..3d98980552 100644 --- a/cmd/soroban-cli/src/commands/contract/invoke.rs +++ b/cmd/soroban-cli/src/commands/contract/invoke.rs @@ -276,6 +276,11 @@ impl Cmd { let network = self.config.get_network()?; tracing::trace!(?network); let contract_id = self.contract_id()?; + let spec_entries = self.spec_entries()?; + if let Some(spec_entries) = &spec_entries { + // For testing wasm arg parsing + let _ = self.build_host_function_parameters(contract_id, spec_entries)?; + } let client = Client::new(&network.rpc_url)?; client .verify_network_passphrase(Some(&network.network_passphrase)) @@ -289,11 +294,7 @@ impl Cmd { let sequence: i64 = account_details.seq_num.into(); // Get the contract - let spec_entries = if let Some(spec) = self.spec_entries()? { - spec - } else { - client.get_remote_contract_spec(&contract_id).await? - }; + let spec_entries = client.get_remote_contract_spec(&contract_id).await?; // Get the ledger footprint let (function, spec, host_function_params, signers) = diff --git a/cmd/soroban-rpc/internal/test/cli_test.go b/cmd/soroban-rpc/internal/test/cli_test.go index 46d5c12b4c..c83df3fcea 100644 --- a/cmd/soroban-rpc/internal/test/cli_test.go +++ b/cmd/soroban-rpc/internal/test/cli_test.go @@ -23,11 +23,10 @@ import ( func cargoTest(t *testing.T, name string) { NewCLITest(t) - c := icmd.Command("cargo", "test", "--package", "soroban-test", "--test", "it", "--", name, "--exact", "--nocapture") + c := icmd.Command("cargo", "test", "--features", "integration", "--package", "soroban-test", "--test", "it", "--", name, "--exact", "--nocapture") c.Env = append(os.Environ(), fmt.Sprintf("SOROBAN_RPC_URL=http://localhost:%d/", sorobanRPCPort), fmt.Sprintf("SOROBAN_NETWORK_PASSPHRASE=%s", StandaloneNetworkPassphrase), - "IGNORED=1", ) res := icmd.RunCmd(c) require.NoError(t, res.Error, res.Stdout(), res.Stderr()) @@ -36,7 +35,7 @@ func cargoTest(t *testing.T, name string) { func TestCLICargoTest(t *testing.T) { names := icmd.RunCmd(icmd.Command("cargo", "-q", "test", "integration::", "--package", "soroban-test", "--features", "integration", "--", "--list")) input := names.Stdout() - lines := strings.Split(input, "\n") + lines := strings.Split(strings.TrimSpace(input), "\n") for _, line := range lines { testName := strings.TrimSuffix(line, ": test") t.Run(testName, func(t *testing.T) {