diff --git a/Cargo.lock b/Cargo.lock index 858cd429..75e66a36 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6213,26 +6213,10 @@ dependencies = [ name = "incredible-squaring-blueprint" version = "0.1.1" dependencies = [ - "ark-bn254 0.4.0", - "ark-ec 0.4.2", - "ark-ff 0.5.0", - "async-trait", "blueprint-metadata", + "blueprint-test-utils", "color-eyre", - "ed25519-zebra 4.0.3", - "eigensdk", "gadget-sdk", - "hex", - "k256", - "libp2p", - "lock_api", - "parking_lot 0.12.3", - "serde_json", - "sp-core", - "subxt-signer", - "tokio", - "tokio-util 0.7.12", - "tracing", ] [[package]] diff --git a/blueprint-test-utils/src/lib.rs b/blueprint-test-utils/src/lib.rs index 03040f08..d4008b07 100644 --- a/blueprint-test-utils/src/lib.rs +++ b/blueprint-test-utils/src/lib.rs @@ -363,23 +363,9 @@ pub fn inject_random_key>(keystore_path: P) -> color_eyre::Result Ok(()) } -/// Returns the output of "git rev-parse --show-toplevel" to get the root of the git repository as a PathBuf. -/// If it's not in a git repo, default to return the current directory pub fn get_blueprint_base_dir() -> PathBuf { - let output = std::process::Command::new("git") - .arg("rev-parse") - .arg("--show-toplevel") - .output() - .expect("Failed to run git command"); - - if output.status.success() { - let path = std::str::from_utf8(&output.stdout) - .expect("Failed to convert output to string") - .trim(); - PathBuf::from(path) - } else { - std::env::current_dir().expect("Failed to get current directory") - } + // TODO: Walk up to find the closest manifest. This only work if `cargo test` is run from the project root. + std::env::current_dir().expect("Failed to get current directory") } pub fn read_cargo_toml_file>(path: P) -> std::io::Result { diff --git a/blueprint-test-utils/src/tangle/mod.rs b/blueprint-test-utils/src/tangle/mod.rs index 8433a3a1..2a0d0581 100644 --- a/blueprint-test-utils/src/tangle/mod.rs +++ b/blueprint-test-utils/src/tangle/mod.rs @@ -9,9 +9,9 @@ pub fn run() -> Result { let tangle_from_env = std::env::var(TANGLE_NODE_ENV).unwrap_or_else(|_| "tangle".to_string()); let builder = SubstrateNode::builder() .binary_paths([ + &tangle_from_env, "../tangle/target/release/tangle", "../../tangle/target/release/tangle", - &tangle_from_env, ]) .arg("validator") .arg_val("rpc-cors", "all") @@ -26,55 +26,21 @@ pub fn run() -> Result { /// A template that makes creating domain-specific macros for tangle-based blueprints easier macro_rules! tangle_blueprint_test_template { ( - $blueprint_path:expr, $N:tt, $test_logic:expr, ) => { - pub use $crate::{ - run_test_blueprint_manager, - Opts, setup_log, - tangle, get_blueprint_base_dir, read_cargo_toml_file, - submit_job, wait_for_completion_of_tangle_job, Job, Args, - }; - use $crate::test_ext::new_test_ext_blueprint_manager; - #[tokio::test(flavor = "multi_thread")] + #[::gadget_sdk::tokio::test(flavor = "multi_thread", crate = "::gadget_sdk::tokio")] async fn test_blueprint() { - setup_log(); - let tangle_node = tangle::run().expect("Failed to start tangle node"); - let mut base_path = get_blueprint_base_dir(); + ::blueprint_test_utils::setup_log(); let tmp_dir = $crate::tempfile::TempDir::new().unwrap(); - let tmp_dir_path = format!("{}", tmp_dir.path().display()); - - base_path.push($blueprint_path); - base_path - .canonicalize() - .expect("File could not be found/normalized"); - - let manifest_path = base_path.join("Cargo.toml"); - log::info!(target: "gadget", "Manifest path: {manifest_path:?}"); - let manifest = read_cargo_toml_file(&manifest_path).expect("Failed to read blueprint's Cargo.toml"); - let blueprint_name = manifest.package.as_ref().unwrap().name.clone(); - - let ws_port = tangle_node.ws_port(); - let http_rpc_url = format!("http://127.0.0.1:{ws_port}"); - let ws_rpc_url = format!("ws://127.0.0.1:{ws_port}"); - - let opts = Opts { - pkg_name: Some(blueprint_name), - http_rpc_url, - ws_rpc_url, - manifest_path, - signer: None, - signer_evm: None, - }; - - new_test_ext_blueprint_manager::<$N, 1, String, _, _>( + let tmp_dir_path = tmp_dir.path().to_string_lossy().into_owned(); + + ::blueprint_test_utils::test_ext::new_test_ext_blueprint_manager::<$N, 1, String, _, _>( tmp_dir_path, - opts, - run_test_blueprint_manager, + ::blueprint_test_utils::run_test_blueprint_manager, ) .await .execute_with_async($test_logic) @@ -86,32 +52,30 @@ macro_rules! tangle_blueprint_test_template { #[macro_export] macro_rules! test_tangle_blueprint { ( - $blueprint_path:expr, $N:tt, $T:tt, $job_id:tt, [$($inputs:expr),*], [$($expected_output:expr),*] ) => { - tangle_blueprint_test_template!( - $blueprint_path, + ::blueprint_test_utils::tangle_blueprint_test_template!( $N, |client, handles, blueprint| async move { let keypair = handles[0].sr25519_id().clone(); let selected_service = &blueprint.services[0]; let service_id = selected_service.id; - gadget_sdk::info!( + ::gadget_sdk::info!( "Submitting job {} with service ID {service_id}", $job_id ); let job_args = vec![$($inputs),*]; - let job = submit_job( + let job = ::blueprint_test_utils::submit_job( client, &keypair, service_id, - Job::from(0), + $job_id as ::blueprint_test_utils::Job, job_args, ) .await @@ -119,11 +83,11 @@ macro_rules! test_tangle_blueprint { let call_id = job.call_id; - gadget_sdk::info!( + ::gadget_sdk::info!( "Submitted job {} with service ID {service_id} has call id {call_id}", $job_id ); - let job_results = wait_for_completion_of_tangle_job(client, service_id, call_id, $T) + let job_results = ::blueprint_test_utils::wait_for_completion_of_tangle_job(client, service_id, call_id, $T) .await .expect("Failed to wait for job completion"); @@ -132,7 +96,7 @@ macro_rules! test_tangle_blueprint { let expected_outputs = vec![$($expected_output),*]; if expected_outputs.is_empty() { - gadget_sdk::info!("No expected outputs specified, skipping verification"); + ::gadget_sdk::info!("No expected outputs specified, skipping verification"); return } @@ -145,27 +109,11 @@ macro_rules! test_tangle_blueprint { ); }; ( - $blueprint_path:expr, $N:tt, $job_id:tt, [$($input:expr),*], [$($expected_output:expr),*] ) => { - test_tangle_blueprint!($blueprint_path, $N, $N, $job_id, [$($input),+], [$($expected_output),+]); + ::blueprint_test_utils::test_tangle_blueprint!($N, $N, $job_id, [$($input),+], [$($expected_output),+]); }; } - -#[cfg(test)] -mod test_incredible_squaring { - use crate::{InputValue, OutputValue}; - - const KEYGEN_JOB_ID: usize = 0; - const N: usize = 5; - test_tangle_blueprint!( - "./blueprints/incredible-squaring/", // Path to the blueprint's dir relative to the git repo root, or, if not in a git repo, the current working directory - N, // Number of nodes - KEYGEN_JOB_ID, // Job ID - [InputValue::Uint64(5)], // Inputs - [OutputValue::Uint64(25)] // Expected output: input squared - ); -} diff --git a/blueprint-test-utils/src/test_ext.rs b/blueprint-test-utils/src/test_ext.rs index 04905942..4ff7bc09 100644 --- a/blueprint-test-utils/src/test_ext.rs +++ b/blueprint-test-utils/src/test_ext.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Tangle. If not, see . -use crate::PerTestNodeInput; +use crate::{get_blueprint_base_dir, read_cargo_toml_file, PerTestNodeInput}; use alloy_primitives::hex; use futures::StreamExt; use blueprint_manager::executor::BlueprintManagerHandle; @@ -41,6 +41,7 @@ use gadget_sdk::{error, info, warn}; use gadget_sdk::clients::tangle::services::{RpcServicesWithBlueprint, ServicesClient}; use gadget_sdk::subxt_core::config::Header; use gadget_sdk::utils::test_utils::get_client; +use crate::tangle::node::SubstrateNode; use crate::tangle::transactions; const LOCAL_BIND_ADDR: &str = "127.0.0.1"; @@ -60,7 +61,6 @@ pub async fn new_test_ext_blueprint_manager< Fut: SendFuture<'static, BlueprintManagerHandle>, >( additional_params: D, - mut opts: Opts, f: F, ) -> LocalhostTestExt { assert!(N > 0, "At least one node is required"); @@ -69,6 +69,31 @@ pub async fn new_test_ext_blueprint_manager< let span = tracing::info_span!("Integration-Test"); let _span = span.enter(); + let base_path = get_blueprint_base_dir(); + let base_path = base_path + .canonicalize() + .expect("File could not be normalized"); + + let manifest_path = base_path.join("Cargo.toml"); + tracing::debug!("Manifest path: {}", manifest_path.display()); + + let manifest = + read_cargo_toml_file(&manifest_path).expect("Failed to read blueprint's Cargo.toml"); + let blueprint_name = manifest.package.as_ref().unwrap().name.clone(); + + tracing::info!("Starting Tangle node..."); + let tangle_node = crate::tangle::run().unwrap(); + tracing::info!("Tangle node running on port: {}", tangle_node.ws_port()); + + let mut opts = Opts { + pkg_name: Some(blueprint_name), + http_rpc_url: format!("http://127.0.0.1:{}", tangle_node.ws_port()), + ws_rpc_url: format!("ws://127.0.0.1:{}", tangle_node.ws_port()), + manifest_path, + signer: None, + signer_evm: None, + }; + let bind_addrs = (0..N) .map(|_| find_open_tcp_bind_port()) .map(|port| { @@ -323,6 +348,7 @@ pub async fn new_test_ext_blueprint_manager< drop(_span); LocalhostTestExt { + _tangle_node: tangle_node, client, handles, span, @@ -342,6 +368,8 @@ pub fn find_open_tcp_bind_port() -> u16 { } pub struct LocalhostTestExt { + // Unused, stored here to keep it from dropping early + _tangle_node: SubstrateNode, client: TangleClient, handles: Vec, span: tracing::Span, diff --git a/blueprints/incredible-squaring/Cargo.toml b/blueprints/incredible-squaring/Cargo.toml index 53811def..fcb824e3 100644 --- a/blueprints/incredible-squaring/Cargo.toml +++ b/blueprints/incredible-squaring/Cargo.toml @@ -10,29 +10,15 @@ repository.workspace = true publish = false [dependencies] -eigensdk = { workspace = true } -tracing = { workspace = true } -async-trait = { workspace = true } gadget-sdk = { workspace = true, features = ["std"] } color-eyre = { workspace = true } -lock_api = { workspace = true } -tokio = { workspace = true, default-features = false, features = ["full"] } -tokio-util = { workspace = true } -sp-core = { workspace = true } -subxt-signer = { workspace = true, features = ["sr25519", "subxt", "std"] } -ark-bn254 = { workspace = true } -ark-ff = { workspace = true } -ark-ec = { workspace = true } -parking_lot = { workspace = true } -libp2p = { workspace = true } -ed25519-zebra = { workspace = true, features = ["pkcs8", "default", "der", "std", "serde", "pem"] } -hex = { workspace = true } -k256 = { workspace = true } -serde_json = { workspace = true } [build-dependencies] blueprint-metadata = { workspace = true } +[dev-dependencies] +blueprint-test-utils.workspace = true + [features] default = ["std"] std = [] diff --git a/blueprints/incredible-squaring/tests/e2e.rs b/blueprints/incredible-squaring/tests/e2e.rs new file mode 100644 index 00000000..7446c2c6 --- /dev/null +++ b/blueprints/incredible-squaring/tests/e2e.rs @@ -0,0 +1,11 @@ +use blueprint_test_utils::{test_tangle_blueprint, InputValue, OutputValue}; + +const SQUARING_JOB_ID: usize = 0; +const N: usize = 5; + +test_tangle_blueprint!( + N, // Number of nodes + SQUARING_JOB_ID, // Job ID + [InputValue::Uint64(5)], // Inputs + [OutputValue::Uint64(25)] // Expected output: input squared +);