#![cfg(target_family = "unix")]
use std::path::PathBuf;
use std::sync::Arc;
use anyhow::{anyhow, bail, Context as _, Result};
use tokio::io::AsyncWriteExt;
use tokio::sync::RwLock;
use tokio::time::Duration;
use wadm_types::{Manifest, Properties};
use wasmcloud_control_interface::{ClientBuilder as CtlClientBuilder, Host};
mod common;
use common::{
find_open_port, init, start_nats, test_dir_with_subfolder, wait_for_no_hosts, wait_for_no_nats,
wait_for_no_wadm,
};
#[tokio::test]
#[serial_test::serial]
async fn integration_dev_hello_component_serial() -> Result<()> {
wait_for_no_hosts()
.await
.context("unexpected wasmcloud instance(s) running")?;
let test_setup = init(
/* component_name= */ "hello",
/* template_name= */ "hello-world-rust",
)
.await?;
let project_dir = test_setup.project_dir.clone();
let dir = test_dir_with_subfolder("dev_hello_component");
wait_for_no_hosts()
.await
.context("one or more unexpected wasmcloud instances running")?;
let nats_port = find_open_port().await?;
let mut nats = start_nats(nats_port, &dir).await?;
let dev_cmd = Arc::new(RwLock::new(
test_setup
.base_command()
.args([
"dev",
"--nats-connect-only",
"--nats-port",
nats_port.to_string().as_ref(),
"--ctl-port",
nats_port.to_string().as_ref(),
"--rpc-port",
nats_port.to_string().as_ref(),
])
.kill_on_drop(true)
.spawn()
.context("failed running cargo dev")?,
));
let watch_dev_cmd = dev_cmd.clone();
let signed_file_path = Arc::new(project_dir.join("build/http_hello_world_s.wasm"));
let expected_path = signed_file_path.clone();
// Wait until the signed file is there (this means dev succeeded)
let _ = tokio::time::timeout(
Duration::from_secs(1200),
tokio::spawn(async move {
loop {
// If the command failed (and exited early), bail
if let Ok(Some(exit_status)) = watch_dev_cmd.write().await.try_wait() {
if !exit_status.success() {
bail!("dev command failed");
}
}
// If the file got built, we know dev succeeded
if expected_path.exists() {
break Ok(());
}
tokio::time::sleep(Duration::from_secs(5)).await;
}
}),
)
.await
.context("timed out while waiting for file path to get created")?;
assert!(signed_file_path.exists(), "signed component file was built");
let process_pid = dev_cmd
.write()
.await
.id()
.context("failed to get child process pid")?;
// Send ctrl + c signal to stop the process
// send SIGINT to the child
nix::sys::signal::kill(
nix::unistd::Pid::from_raw(process_pid as i32),
nix::sys::signal::Signal::SIGINT,
)
.expect("cannot send ctrl-c");
// Wait until the process stops
let _ = tokio::time::timeout(Duration::from_secs(15), dev_cmd.write().await.wait())
.await
.context("dev command did not exit")?;
wait_for_no_hosts()
.await
.context("wasmcloud instance failed to exit cleanly (processes still left over)")?;
// Kill the nats instance
nats.kill().await.map_err(|e| anyhow!(e))?;
wait_for_no_nats()
.await
.context("nats instance failed to exit cleanly (processes still left over)")?;
wait_for_no_wadm()
.await
.context("wadm instance failed to exit cleanly (processes still left over)")?;
Ok(())
}
/// Ensure that overriding manifest YAML works
#[tokio::test]
#[serial_test::serial]
async fn integration_override_manifest_yaml_serial() -> Result<()> {
wait_for_no_hosts()
.await
.context("unexpected wasmcloud instance(s) running")?;
let test_setup = init("hello", "hello-world-rust").await?;
let project_dir = test_setup.project_dir.clone();
let dir = test_dir_with_subfolder("dev_hello_component");
wait_for_no_hosts()
.await
.context("one or more unexpected wasmcloud instances running")?;
// Start NATS
let nats_port = find_open_port().await?;
let mut nats = start_nats(nats_port, &dir).await?;
// Create a ctl client to check the cluster
let ctl_client = CtlClientBuilder::new(
async_nats::connect(format!("127.0.0.1:{nats_port}"))
.await
.context("failed to create nats client")?,
)
.lattice("default")
.build();
// Write out the fixture configuration to disk
let fixture_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("./tests/fixtures/wadm/hello-world-rust-dev-override.yaml");
tokio::fs::write(
project_dir.join("test.wadm.yaml"),
tokio::fs::read(&fixture_path)
.await
.with_context(|| format!("failed to read fixture @ [{}]", fixture_path.display()))?,
)
.await
.context("failed to write out fixture file")?;
// Manipulate the wasmcloud.toml for the test project and override the manifest
let wasmcloud_toml_path = project_dir.join("wasmcloud.toml");
let mut wasmcloud_toml = tokio::fs::File::options()
.append(true)
.open(&wasmcloud_toml_path)
.await
.with_context(|| {
format!(
"failed to open wasmcloud toml file @ [{}]",
wasmcloud_toml_path.display()
)
})?;
wasmcloud_toml
.write_all(
r#"
[dev]
manifests = [
{ component_name = "http-handler", path = "test.wadm.yaml" }
]
"#
.as_bytes(),
)
.await
.context("failed tow write dev configuration content to file")?;
wasmcloud_toml.flush().await?;
// Run wash dev
let dev_cmd = Arc::new(RwLock::new(
test_setup
.base_command()
.args([
"dev",
"--nats-port",
nats_port.to_string().as_ref(),
"--nats-connect-only",
"--ctl-port",
nats_port.to_string().as_ref(),
"--rpc-port",
nats_port.to_string().as_ref(),
])
.kill_on_drop(true)
.spawn()
.context("failed running cargo dev")?,
));
let watch_dev_cmd = dev_cmd.clone();
// Get the host that was created
let host = tokio::time::timeout(Duration::from_secs(10), async {
loop {
if let Some(h) = ctl_client
.get_hosts()
.await
.map_err(|e| anyhow!("failed to get hosts: {e}"))
.context("get components")?
.into_iter()
.map(|v| v.into_data())
.next()
{
return Ok::