From d196bf1e927ec8e07447f13d9cfbf201a08b288e Mon Sep 17 00:00:00 2001 From: stevelr Date: Mon, 20 Feb 2023 11:33:57 -0800 Subject: [PATCH 1/6] use smithy-bindgen to gen interface, re-export as 'testing' Signed-off-by: stevelr --- wasmcloud-test-util/Cargo.toml | 7 +-- wasmcloud-test-util/src/cli.rs | 2 +- wasmcloud-test-util/src/lib.rs | 58 ++++++++++++++++++++++-- wasmcloud-test-util/src/provider_test.rs | 16 ++++--- 4 files changed, 70 insertions(+), 13 deletions(-) diff --git a/wasmcloud-test-util/Cargo.toml b/wasmcloud-test-util/Cargo.toml index 3214957..ef8ce54 100644 --- a/wasmcloud-test-util/Cargo.toml +++ b/wasmcloud-test-util/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmcloud-test-util" -version = "0.6.4" +version = "0.6.5" edition = "2021" authors = [ "wasmcloud Team" ] license = "Apache-2.0" @@ -10,8 +10,9 @@ repository = "https://github.com/wasmcloud/wasmcloud-test" readme = "README.md" [dependencies] -wasmcloud-interface-testing = "0.7.1" -wasmbus-rpc = "0.11.2" +smithy-bindgen = { git="https://github.com/wasmcloud/weld", branch="feat/smithy-bindgen" } +wasmbus-rpc = { git="https://github.com/wasmcloud/weld", rev="4faec462d1dd41efbfe95c9e2d2061a9a40f2fcc", features = [ "otel" ] } +serde_bytes = "0.11" regex = "1" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] diff --git a/wasmcloud-test-util/src/cli.rs b/wasmcloud-test-util/src/cli.rs index 55b4e33..fa07d43 100644 --- a/wasmcloud-test-util/src/cli.rs +++ b/wasmcloud-test-util/src/cli.rs @@ -1,9 +1,9 @@ //! cli utilities use std::io::Write; +use crate::testing::TestResult; use serde::Deserialize; use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; -use wasmcloud_interface_testing::TestResult; // structure for deserializing error results #[derive(Deserialize)] diff --git a/wasmcloud-test-util/src/lib.rs b/wasmcloud-test-util/src/lib.rs index 30642f0..9c91b0e 100644 --- a/wasmcloud-test-util/src/lib.rs +++ b/wasmcloud-test-util/src/lib.rs @@ -4,13 +4,65 @@ pub mod provider_test; #[cfg(not(target_arch = "wasm32"))] pub mod cli; -// re-export testing interface -pub use wasmcloud_interface_testing as testing; - // re-export regex and nkeys pub use nkeys; pub use regex; +#[allow(dead_code)] +pub mod testing { + + smithy_bindgen::smithy_bindgen!("testing/testing.smithy", "org.wasmcloud.interface.testing"); + + // after sdk is split we won't have to duplicate this code + impl Default for TestOptions { + fn default() -> TestOptions { + TestOptions { + patterns: vec![".*".to_string()], + options: std::collections::HashMap::default(), + } + } + } + + pub type NamedResult<'name, T> = (&'name str, RpcResult); + + // convert empty RpcResult into a testResult + impl<'name, T: Serialize> From> for TestResult { + fn from(name_res: NamedResult<'name, T>) -> TestResult { + match name_res.1 { + Ok(res) => { + // TODO: if serialization of data fails, it doesn't change + // the test result, but serialization errors should be logged. + // Logging requires us to have logging set up + // (we might be running in an actor) + let data = match serde_json::to_vec(&res) { + Ok(v) => serde_json::to_vec(&serde_json::json!({ "data": v })) + .unwrap_or_default(), + Err(_) => b"".to_vec(), + }; + TestResult { + name: name_res.0.to_string(), + passed: true, + snap_data: Some(data), + } + } + Err(e) => { + let data = serde_json::to_vec(&serde_json::json!( + { + "error": e.to_string(), + } + )) + .ok(); + TestResult { + name: name_res.0.to_string(), + passed: false, + snap_data: data, + } + } + } + } + } +} + // these macros are supported on all build targets (wasm32 et. al.) /// check that the two expressions are equal, returning RpcError if they are not diff --git a/wasmcloud-test-util/src/provider_test.rs b/wasmcloud-test-util/src/provider_test.rs index 65f1c6a..1461270 100644 --- a/wasmcloud-test-util/src/provider_test.rs +++ b/wasmcloud-test-util/src/provider_test.rs @@ -2,7 +2,6 @@ //! //! simple test harness to load a capability provider and test it //! -use crate::testing::TestResult; use anyhow::anyhow; use async_trait::async_trait; use futures::future::BoxFuture; @@ -83,6 +82,11 @@ pub struct ProviderProcess { } impl ProviderProcess { + /// Returns the nats topic used by a mock actor + pub fn mock_actor_rpc_topic(&self) -> String { + wasmbus_rpc::rpc_client::rpc_topic(&self.origin(), &self.host_data.lattice_rpc_prefix) + } + /// generate the `origin` field for an Invocation. To the receiving provider, /// the origin field looks like an actor pub fn origin(&self) -> WasmCloudEntity { @@ -404,7 +408,7 @@ macro_rules! run_selected_spawn { let handle = tokio::runtime::Handle::current(); let all_tests = vec![".*".to_string()]; let pats : &Vec = $opt.patterns.as_ref(); - let mut results: Vec = Vec::new(); + let mut results = Vec::new(); // Each test case regex (pats) is checked against all test names (tname). // This would be simpler to use a RegexSet, but then the tests would @@ -432,7 +436,7 @@ macro_rules! run_selected_spawn { $tname(&opts).await } ).await; - let tr:TestResult = match join { + let tr: testing::TestResult = match join { Ok(res) => (name, res).into(), Err(e) => (name, Err::<(),RpcError>( RpcError::Other(format!("join error: {}", e.to_string())) @@ -462,12 +466,12 @@ macro_rules! run_selected_spawn { // calls that fail. pub async fn run_tests( tests: Vec<(&'static str, TestFunc)>, -) -> std::result::Result, Box> { - let mut results: Vec = Vec::new(); +) -> std::result::Result, Box> { + let mut results = Vec::new(); let handle = tokio::runtime::Handle::current(); for (name, tfunc) in tests.into_iter() { let rc: RpcResult<()> = handle.spawn(tfunc()).await?; - results.push(TestResult { + results.push(crate::testing::TestResult { name: name.to_string(), passed: rc.is_ok(), ..Default::default() From f1a25050e53a043491fa8233e2286d386dc3d030 Mon Sep 17 00:00:00 2001 From: stevelr Date: Tue, 21 Feb 2023 15:20:27 -0800 Subject: [PATCH 2/6] minor cleanup Signed-off-by: stevelr --- wasmcloud-test-util/src/lib.rs | 1 - wasmcloud-test-util/src/provider_test.rs | 47 +++++++++++++++--------- 2 files changed, 29 insertions(+), 19 deletions(-) diff --git a/wasmcloud-test-util/src/lib.rs b/wasmcloud-test-util/src/lib.rs index 9c91b0e..c13398d 100644 --- a/wasmcloud-test-util/src/lib.rs +++ b/wasmcloud-test-util/src/lib.rs @@ -8,7 +8,6 @@ pub mod cli; pub use nkeys; pub use regex; -#[allow(dead_code)] pub mod testing { smithy_bindgen::smithy_bindgen!("testing/testing.smithy", "org.wasmcloud.interface.testing"); diff --git a/wasmcloud-test-util/src/provider_test.rs b/wasmcloud-test-util/src/provider_test.rs index 1461270..870c073 100644 --- a/wasmcloud-test-util/src/provider_test.rs +++ b/wasmcloud-test-util/src/provider_test.rs @@ -2,12 +2,21 @@ //! //! simple test harness to load a capability provider and test it //! +use crate::testing::TestResult; use anyhow::anyhow; use async_trait::async_trait; use futures::future::BoxFuture; use nkeys::{KeyPair, KeyPairType}; use serde::Serialize; -use std::{fs, io::Write, ops::Deref, path::PathBuf, sync::Arc}; +use std::{ + fs, + io::Write, + ops::Deref, + path::{Path, PathBuf}, + process, + sync::{Arc, Mutex}, + time::Duration, +}; use tokio::sync::OnceCell; use toml::value::Value as TomlValue; use wasmbus_rpc::{ @@ -50,7 +59,7 @@ fn to_value_map(data: &toml::map::Map) -> RpcResult, + pub timeout_ms: Mutex, } impl ProviderProcess { @@ -214,7 +223,7 @@ impl ProviderProcess { if !resp.is_empty() { eprintln!("shutdown response: {}", String::from_utf8_lossy(&resp)); } - tokio::time::sleep(std::time::Duration::from_secs(2)).await; + tokio::time::sleep(Duration::from_secs(2)).await; Ok(()) } } @@ -226,13 +235,13 @@ impl Transport for Provider { _ctx: &Context, message: Message<'_>, _opts: Option, - ) -> std::result::Result, RpcError> { + ) -> Result, RpcError> { self.inner.send_rpc(message).await } /// sets the time period for an expected response to rpc messages, /// after which an RpcError::Timeout will occur. - fn set_timeout(&self, interval: std::time::Duration) { + fn set_timeout(&self, interval: Duration) { let lock = self.timeout_ms.try_lock(); if let Ok(mut rg) = lock { *rg = interval.as_millis() as u64 @@ -300,7 +309,7 @@ pub fn load_config() -> Result { /// or in the config file as "par_file" pub async fn start_provider_test( config: TomlMap, - exe_path: &std::path::Path, + exe_path: &Path, ld: LinkDefinition, ) -> Result { let exe_file = fs::File::open(exe_path)?; @@ -347,9 +356,9 @@ pub async fn start_provider_test( encoded.push_str("\r\n"); // provider's stdout is piped through our stdout - let mut child_proc = std::process::Command::new(exe_path) - .stdout(std::process::Stdio::piped()) - .stdin(std::process::Stdio::piped()) + let mut child_proc = process::Command::new(exe_path) + .stdout(process::Stdio::piped()) + .stdin(process::Stdio::piped()) .env("RUST_LOG", &log_level) .env("RUST_BACKTRACE", enable_backtrace) .spawn() @@ -383,13 +392,13 @@ pub async fn start_provider_test( rpc_client: RpcClient::new( nats_client, host_key.public_key(), - Some(std::time::Duration::from_millis( + Some(Duration::from_millis( host_data.default_rpc_timeout_ms.unwrap_or(2000), )), Arc::new(host_key), ), host_data, - timeout_ms: std::sync::Mutex::new(DEFAULT_RPC_TIMEOUT_MILLIS), + timeout_ms: Mutex::new(DEFAULT_RPC_TIMEOUT_MILLIS), }), }) } @@ -401,6 +410,7 @@ pub async fn start_provider_test( /// This is like the `run_selected!` macro, except that it spawns /// a thread for running the test case, so it can handle panics /// (and failed assertions, which panic). +/// Users of this macro must have a the testing interface package in scope by using `use wasmcloud_test_util::testing`; #[macro_export] macro_rules! run_selected_spawn { ( $opt:expr, $($tname:ident),* $(,)? ) => {{ @@ -464,14 +474,15 @@ macro_rules! run_selected_spawn { // all test cases in the current thread (async executor). // The reason I had put the spawn in was to catch panics from assert // calls that fail. +/// Users of this macro must have a the testing interface package in scope by using `use wasmcloud_test_util::testing`; pub async fn run_tests( tests: Vec<(&'static str, TestFunc)>, -) -> std::result::Result, Box> { +) -> Result, Box> { let mut results = Vec::new(); let handle = tokio::runtime::Handle::current(); for (name, tfunc) in tests.into_iter() { let rc: RpcResult<()> = handle.spawn(tfunc()).await?; - results.push(crate::testing::TestResult { + results.push(TestResult { name: name.to_string(), passed: rc.is_ok(), ..Default::default() @@ -547,14 +558,14 @@ pub async fn load_provider() -> Result { } None => DEFAULT_START_DELAY_SEC, }; - tokio::time::sleep(std::time::Duration::from_secs(delay_time_sec)).await; + tokio::time::sleep(Duration::from_secs(delay_time_sec)).await; // optionally, allow extra time to handle put_link if let Some(n) = prov.config.get("link_delay_sec") { if let Some(n) = n.as_integer() { if n > 0 { eprintln!("Pausing {} secs after put_link", n); - tokio::time::sleep(std::time::Duration::from_secs(n as u64)).await; + tokio::time::sleep(Duration::from_secs(n as u64)).await; } } else { return Err(RpcError::InvalidParameter(format!( From 8171ee40fc4e91507366f804b3e39786e3af0c20 Mon Sep 17 00:00:00 2001 From: stevelr Date: Tue, 21 Feb 2023 15:40:51 -0800 Subject: [PATCH 3/6] improve comments on result handling Signed-off-by: stevelr --- wasmcloud-test-util/src/lib.rs | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/wasmcloud-test-util/src/lib.rs b/wasmcloud-test-util/src/lib.rs index c13398d..bee727a 100644 --- a/wasmcloud-test-util/src/lib.rs +++ b/wasmcloud-test-util/src/lib.rs @@ -22,29 +22,37 @@ pub mod testing { } } + /// A NamedResult, generated inside a `run_selected!`` or `run_selected_spawn!`` macro, + /// contains a tuple of a test case name and its result. The implementation of From here + /// makes it easy to use the `into()` function to turn the NamedResult into a TestResult. pub type NamedResult<'name, T> = (&'name str, RpcResult); // convert empty RpcResult into a testResult impl<'name, T: Serialize> From> for TestResult { fn from(name_res: NamedResult<'name, T>) -> TestResult { - match name_res.1 { + let test_case_name = name_res.0.to_string(); + let test_case_result = name_res.1; + match test_case_result { Ok(res) => { - // TODO: if serialization of data fails, it doesn't change - // the test result, but serialization errors should be logged. - // Logging requires us to have logging set up - // (we might be running in an actor) + // test passed. Serialize the data to json let data = match serde_json::to_vec(&res) { Ok(v) => serde_json::to_vec(&serde_json::json!({ "data": v })) .unwrap_or_default(), + // if serialization of data fails, it doesn't change + // the test result, but serialization errors should be logged. + // Logging requires us to have logging set up, but since we might be running as an actor, + // and we can't force the user to add a logging dependency and set up logging, + // so there isn't much we can do here. Not even println!(). Err(_) => b"".to_vec(), }; TestResult { - name: name_res.0.to_string(), + name: test_case_name, passed: true, snap_data: Some(data), } } Err(e) => { + // test failed: generate an error message let data = serde_json::to_vec(&serde_json::json!( { "error": e.to_string(), @@ -52,7 +60,7 @@ pub mod testing { )) .ok(); TestResult { - name: name_res.0.to_string(), + name: test_case_name, passed: false, snap_data: data, } From 1536328bb8aa39779a0e6eecd121bb46cde76c68 Mon Sep 17 00:00:00 2001 From: stevelr Date: Mon, 27 Feb 2023 08:04:44 -0800 Subject: [PATCH 4/6] Update wasmcloud-test-util/src/lib.rs Co-authored-by: Roman Volosatovs --- wasmcloud-test-util/src/lib.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/wasmcloud-test-util/src/lib.rs b/wasmcloud-test-util/src/lib.rs index bee727a..6c9b82b 100644 --- a/wasmcloud-test-util/src/lib.rs +++ b/wasmcloud-test-util/src/lib.rs @@ -29,10 +29,9 @@ pub mod testing { // convert empty RpcResult into a testResult impl<'name, T: Serialize> From> for TestResult { - fn from(name_res: NamedResult<'name, T>) -> TestResult { - let test_case_name = name_res.0.to_string(); - let test_case_result = name_res.1; - match test_case_result { + fn from((name, res): NamedResult<'name, T>) -> TestResult { + let name = name.into(); + match res { Ok(res) => { // test passed. Serialize the data to json let data = match serde_json::to_vec(&res) { @@ -46,7 +45,7 @@ pub mod testing { Err(_) => b"".to_vec(), }; TestResult { - name: test_case_name, + name, passed: true, snap_data: Some(data), } @@ -60,7 +59,7 @@ pub mod testing { )) .ok(); TestResult { - name: test_case_name, + name, passed: false, snap_data: data, } From 149875ab0548d21965fbefa21fb366775ae36d13 Mon Sep 17 00:00:00 2001 From: stevelr Date: Mon, 27 Feb 2023 18:00:59 -0800 Subject: [PATCH 5/6] update dependencies, bump to 0.7.0 Signed-off-by: stevelr --- wasmcloud-test-util/Cargo.toml | 11 +++++------ wasmcloud-test-util/src/provider_test.rs | 6 ++++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/wasmcloud-test-util/Cargo.toml b/wasmcloud-test-util/Cargo.toml index ef8ce54..6d300fc 100644 --- a/wasmcloud-test-util/Cargo.toml +++ b/wasmcloud-test-util/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasmcloud-test-util" -version = "0.6.5" +version = "0.7.0" edition = "2021" authors = [ "wasmcloud Team" ] license = "Apache-2.0" @@ -10,21 +10,20 @@ repository = "https://github.com/wasmcloud/wasmcloud-test" readme = "README.md" [dependencies] -smithy-bindgen = { git="https://github.com/wasmcloud/weld", branch="feat/smithy-bindgen" } -wasmbus-rpc = { git="https://github.com/wasmcloud/weld", rev="4faec462d1dd41efbfe95c9e2d2061a9a40f2fcc", features = [ "otel" ] } +smithy-bindgen = "0.1.0" +wasmbus-rpc = { version = "0.12", features = [ "otel" ] } serde_bytes = "0.11" regex = "1" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] anyhow = "1.0" async-trait = "0.1" -async-nats = "0.23.0" futures = "0.3" -base64 = "0.13" +base64 = "0.21" log = "0.4" nkeys = "0.2.0" serde = { version = "1.0", features=["derive"]} serde_json = "1.0" termcolor = "1.1" tokio = { version = "1", features = ["full"]} -toml = "0.5" +toml = "0.7" diff --git a/wasmcloud-test-util/src/provider_test.rs b/wasmcloud-test-util/src/provider_test.rs index 870c073..721238f 100644 --- a/wasmcloud-test-util/src/provider_test.rs +++ b/wasmcloud-test-util/src/provider_test.rs @@ -5,6 +5,7 @@ use crate::testing::TestResult; use anyhow::anyhow; use async_trait::async_trait; +use base64::{engine::general_purpose::STANDARD_NO_PAD, engine::Engine}; use futures::future::BoxFuture; use nkeys::{KeyPair, KeyPairType}; use serde::Serialize; @@ -20,6 +21,7 @@ use std::{ use tokio::sync::OnceCell; use toml::value::Value as TomlValue; use wasmbus_rpc::{ + async_nats, common::{Context, Message, SendOpts, Transport}, core::{HealthCheckRequest, HealthCheckResponse, HostData, LinkDefinition, WasmCloudEntity}, error::{RpcError, RpcResult}, @@ -59,7 +61,7 @@ fn to_value_map(data: &toml::map::Map) -> RpcResult Date: Thu, 23 Mar 2023 12:01:30 -0700 Subject: [PATCH 6/6] add wasmcloud_test_util prefix inside macro Signed-off-by: stevelr --- wasmcloud-test-util/src/provider_test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wasmcloud-test-util/src/provider_test.rs b/wasmcloud-test-util/src/provider_test.rs index 721238f..5f720ac 100644 --- a/wasmcloud-test-util/src/provider_test.rs +++ b/wasmcloud-test-util/src/provider_test.rs @@ -448,7 +448,7 @@ macro_rules! run_selected_spawn { $tname(&opts).await } ).await; - let tr: testing::TestResult = match join { + let tr: wasmcloud_test_util::testing::TestResult = match join { Ok(res) => (name, res).into(), Err(e) => (name, Err::<(),RpcError>( RpcError::Other(format!("join error: {}", e.to_string()))