Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions wasmcloud-test-util/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -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]
Expand Down
2 changes: 1 addition & 1 deletion wasmcloud-test-util/src/cli.rs
Original file line number Diff line number Diff line change
@@ -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)]
Expand Down
65 changes: 62 additions & 3 deletions wasmcloud-test-util/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,72 @@ 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;

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(),
}
}
}

/// 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<T>);

// convert empty RpcResult into a testResult
impl<'name, T: Serialize> From<NamedResult<'name, T>> 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 {
Ok(res) => {
// 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: 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(),
}
))
.ok();
TestResult {
name: test_case_name,
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
Expand Down
55 changes: 35 additions & 20 deletions wasmcloud-test-util/src/provider_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,15 @@ 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::{
Expand Down Expand Up @@ -51,7 +59,7 @@ fn to_value_map(data: &toml::map::Map<String, TomlValue>) -> RpcResult<SimpleVal
// copy the entire map as base64-encoded json with value "config_b64"
let json = serde_json::to_string(data)
.map_err(|e| RpcError::Ser(format!("invalid 'values' map: {}", e)))?;
let b64 = base64::encode_config(&json, base64::STANDARD_NO_PAD);
let b64 = base64::encode_config(json, base64::STANDARD_NO_PAD);
map.insert("config_b64".to_string(), b64);
Ok(map)
}
Expand All @@ -71,18 +79,23 @@ struct ShutdownMessage {
/// the provider will exit
#[derive(Debug)]
pub struct ProviderProcess {
pub file: std::fs::File,
pub file: fs::File,
pub host_data: HostData,
pub actor_id: String,
pub path: PathBuf,
pub proc: std::process::Child,
pub proc: process::Child,
pub config: TomlMap,
pub nats_client: async_nats::Client,
pub rpc_client: RpcClient,
pub timeout_ms: std::sync::Mutex<u64>,
pub timeout_ms: Mutex<u64>,
}

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 {
Expand Down Expand Up @@ -210,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(())
}
}
Expand All @@ -222,13 +235,13 @@ impl Transport for Provider {
_ctx: &Context,
message: Message<'_>,
_opts: Option<SendOpts>,
) -> std::result::Result<Vec<u8>, RpcError> {
) -> Result<Vec<u8>, 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
Expand Down Expand Up @@ -296,7 +309,7 @@ pub fn load_config() -> Result<TomlMap, RpcError> {
/// 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<Provider, RpcError> {
let exe_file = fs::File::open(exe_path)?;
Expand Down Expand Up @@ -343,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()
Expand Down Expand Up @@ -379,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),
}),
})
}
Expand All @@ -397,14 +410,15 @@ 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),* $(,)? ) => {{
let mut unique = std::collections::BTreeSet::new();
let handle = tokio::runtime::Handle::current();
let all_tests = vec![".*".to_string()];
let pats : &Vec<String> = $opt.patterns.as_ref();
let mut results: Vec<TestResult> = 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
Expand Down Expand Up @@ -432,7 +446,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()))
Expand Down Expand Up @@ -460,10 +474,11 @@ 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<Vec<TestResult>, Box<dyn std::error::Error>> {
let mut results: Vec<TestResult> = Vec::new();
) -> Result<Vec<TestResult>, Box<dyn std::error::Error>> {
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?;
Expand Down Expand Up @@ -543,14 +558,14 @@ pub async fn load_provider() -> Result<Provider, RpcError> {
}
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!(
Expand Down