use anyhow::{ensure, Context};
use tokio::process::Command;
use tracing::instrument;
mod common;
#[cfg(feature = "nats")]
#[instrument(ret)]
#[test_log::test(tokio::test(flavor = "multi_thread"))]
async fn go_bindgen() -> anyhow::Result<()> {
use core::time::Duration;
use anyhow::{anyhow, bail};
use common::assert_async;
use tokio::fs;
use tokio::time::sleep;
use tracing::info;
if let Err(err) = fs::remove_dir_all("tests/go/bindings").await {
match err.kind() {
std::io::ErrorKind::NotFound => {}
_ => bail!(anyhow!(err).context("failed to remove `test/go/bindings`")),
}
}
let status = Command::new("go")
.current_dir("tests/go")
.args(["generate", "./..."])
.env("WIT_BINDGEN_WRPC", env!("CARGO_BIN_EXE_wit-bindgen-wrpc"))
.kill_on_drop(true)
.status()
.await
.context("failed to call `go generate`")?;
ensure!(status.success(), "`go generate` failed");
let status = Command::new("go")
.current_dir("tests/go")
.args(["test", "-v", "-failfast"])
.kill_on_drop(true)
.status()
.await
.context("failed to call `go test`")?;
ensure!(status.success(), "`go test` failed");
wrpc_test::with_nats(|port, nats_client| async move {
mod bindings {
wit_bindgen_wrpc::generate!({
world: "sync-client",
path: "tests/wit",
additional_derives: [::core::cmp::PartialEq],
});
}
use bindings::foo;
use bindings::wrpc_test::integration::sync;
use bindings::wrpc_test::integration::sync::{Abc, Foobar, Rec, RecNested, Var};
info!("starting `sync-server-nats`");
let mut server = Command::new("go")
.current_dir("tests/go")
.args([
"run",
"./cmd/sync-server-nats",
&format!("nats://localhost:{port}"),
])
.kill_on_drop(true)
.spawn()
.context("failed to run `sync-server-nats`")?;
let client = wrpc_transport_nats::Client::new(nats_client, "go", None)
.await
.context("failed to construct client")?;
sleep(Duration::from_secs(1)).await;
info!("calling `wrpc-test:integration/sync-client.foo.f`");
let v = foo::f(&client, None, "f")
.await
.context("failed to call `wrpc-test:integration/sync-client.foo.f`")?;
ensure!(v == 42);
info!("calling `wrpc-test:integration/sync-client.foo.foo`");
foo::foo(&client, None, "foo")
.await
.context("failed to call `wrpc-test:integration/sync-client.foo.foo`")?;
info!("calling `wrpc-test:integration/sync.fallible`");
let res = sync::fallible(&client, None, true)
.await
.context("failed to call `wrpc-test:integration/sync.fallible`")?;
ensure!(res == Ok(true));
info!("calling `wrpc-test:integration/sync.fallible`");
let res = sync::fallible(&client, None, false)
.await
.context("failed to call `wrpc-test:integration/sync.fallible`")?;
ensure!(res == Err("test".to_string()));
info!("calling `wrpc-test:integration/sync.numbers`");
let (a, b, c, d, e, f, g, h, i, j) = sync::numbers(&client, None)
.await
.context("failed to call `wrpc-test:integration/sync.numbers`")?;
ensure!(a == 1);
ensure!(b == 2);
ensure!(c == 3);
ensure!(d == 4);
ensure!(e == 5);
ensure!(f == 6);
ensure!(g == 7);
ensure!(h == 8);
ensure!(i == 9.);
ensure!(j == 10.);
info!("calling `wrpc-test:integration/sync.with-flags`");
let v = sync::with_flags(&client, None, true, false, true)
.await
.context("failed to call `wrpc-test:integration/sync.with-flags`")?;
ensure!(v == Abc::A | Abc::C, "{v:?}");
info!("calling `wrpc-test:integration/sync.with-variant-option`");
let v = sync::with_variant_option(&client, None, false)
.await
.context("failed to call `wrpc-test:integration/sync.with-variant-option`")?;
ensure!(v.is_none(), "{v:?}");
info!("calling `wrpc-test:integration/sync.with-variant-option`");
let v = sync::with_variant_option(&client, None, true)
.await
.context("failed to call `wrpc-test:integration/sync.with-variant-option`")?;
ensure!(
v == Some(Var::Var(Rec {
nested: RecNested {
foo: "bar".to_string()
}
})),
"{v:?}"
);
info!("calling `wrpc-test:integration/sync.with-variant-list`");
let v = sync::with_variant_list(&client, None)
.await
.context("failed to call `wrpc-test:integration/sync.with-variant-list`")?;
ensure!(
v == [
Var::Empty,
Var::Var(Rec {
nested: RecNested {
foo: "foo".to_string()
}
}),
Var::Empty,
Var::Empty,
Var::Empty,
Var::Var(Rec {
nested: RecNested {
foo: "bar".to_string()
}
}),
],
"{v:?}"
);
info!("calling `wrpc-test:integration/sync.with-record`");
let v = sync::with_record(&client, None)
.await
.context("failed to call `wrpc-test:integration/sync.with-record`")?;
ensure!(
v == Rec {
nested: RecNested {
foo: "foo".to_string()
}
},
"{v:?}"
);
info!("calling `wrpc-test:integration/sync.with-record-list`");
let v = sync::with_record_list(&client, None, 0)
.await
.context("failed to call `wrpc-test:integration/sync.with-record-list`")?;
ensure!(v.is_empty(), "{v:?}");
info!("calling `wrpc-test:integration/sync.with-record-list`");
let v = sync::with_record_list(&client, None, 3)
.await
.context("failed to call `wrpc-test:integration/sync.with-record-list`")?;
ensure!(
v == [
Rec {
nested: RecNested {
foo: "0".to_string()
}
},
Rec {
nested: RecNested {
foo: "1".to_string()
}
},
Rec {
nested: RecNested {
foo: "2".to_string()
}
},
],
"{v:?}",
);
info!("calling `wrpc-test:integration/sync.with-record-tuple`");
let v = sync::with_record_tuple(&client, None)
.await
.context("failed to call `wrpc-test:integration/sync.with-record-tuple`")?;
ensure!(
v == (
Rec {
nested: RecNested {
foo: "0".to_string()
}
},
Rec {
nested: RecNested {
foo: "1".to_string()
}
},
),
"{v:?}",
);
info!("calling `wrpc-test:integration/sync.with-enum`");
let v = sync::with_enum(&client, None)
.await
.context("failed to call `wrpc-test:integration/sync.with-enum-tuple`")?;
ensure!(v == Foobar::Bar, "{v:?}",);
server
.start_kill()
.context("failed to kill `sync-server-nats`")?;
server
.wait_with_output()
.await
.context("failed to wait for `sync-server-nats` to exit")?;
Ok(())
})
.await?;
wrpc_test::with_nats(|port, nats_client| async move {
info!("starting `async-server-nats`");
let mut server = Command::new("go")
.current_dir("tests/go")
.args([
"run",
"./cmd/async-server-nats",
&format!("nats://localhost:{port}"),
])
.kill_on_drop(true)
.spawn()
.context("failed to run `async-server-nats`")?;
sleep(Duration::from_secs(1)).await;
let client = wrpc_transport_nats::Client::new(nats_client, "go", None)
.await
.context("failed to construct client")?;
assert_async(&client).await?;
server
.start_kill()
.context("failed to kill `async-server-nats`")?;
server
.wait_with_output()
.await
.context("failed to wait for `async-server-nats` to exit")?;
Ok(())
})
.await?;
Ok(())
}
#[instrument(ret)]
#[test_log::test(tokio::test(flavor = "multi_thread"))]
async fn go() -> anyhow::Result<()> {
let status = Command::new("go")
.current_dir("go")
.args(["test", "-v", "-failfast", "./..."])
.kill_on_drop(true)
.status()
.await
.context("failed to call `go test`")?;
ensure!(status.success(), "`go test` failed");
Ok(())
}