test_r::enable!();
#[cfg(feature = "export_golem_agentic")]
mod tests {
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;
use test_r::test;
#[test]
fn agent_implementation_annotation_ui_tests() {
let t = trybuild::TestCases::new();
t.compile_fail("tests/ui/fail/*.rs");
t.pass("tests/ui/pass/*.rs");
}
#[test]
fn agent_definition_implementation_and_client_can_live_in_separate_crates() {
let workspace = create_cross_crate_workspace();
let target_dir = Path::new(env!("CARGO_MANIFEST_DIR"))
.parent()
.expect("golem-rust crate should have an SDK workspace parent")
.join("target");
let output = Command::new("cargo")
.arg("check")
.arg("--workspace")
.arg("--quiet")
.env("CARGO_TARGET_DIR", target_dir)
.current_dir(&workspace)
.output()
.expect("failed to run cargo check for cross-crate agent workspace");
fs::remove_dir_all(&workspace).unwrap_or_else(|error| {
panic!(
"failed to remove temporary workspace {}: {error}",
workspace.display()
)
});
if !output.status.success() {
panic!(
"cross-crate agent workspace failed to compile\nstatus: {}\nstdout:\n{}\nstderr:\n{}",
output.status,
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr),
);
}
}
fn create_cross_crate_workspace() -> PathBuf {
let root = std::env::temp_dir().join(format!(
"golem-rust-agent-ui-{}-{}",
std::process::id(),
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.expect("system clock should be after UNIX_EPOCH")
.as_nanos()
));
fs::create_dir_all(root.join("agent-api/src")).unwrap();
fs::create_dir_all(root.join("agent-impl/src")).unwrap();
fs::create_dir_all(root.join("agent-client/src")).unwrap();
let golem_rust_path = Path::new(env!("CARGO_MANIFEST_DIR"));
fs::write(
root.join("Cargo.toml"),
r#"
[workspace]
resolver = "2"
members = ["agent-api", "agent-impl", "agent-client"]
"#,
)
.unwrap();
write_crate_manifest(
&root,
"agent-api",
&format!(
r#"
[dependencies]
golem-rust = {{ path = {}, features = ["export_golem_agentic"] }}
"#,
toml_string(golem_rust_path)
),
);
fs::write(
root.join("agent-api/src/lib.rs"),
r#"
use golem_rust::agent_definition;
#[agent_definition]
pub trait CrossCrateAgent {
fn new(id: String) -> Self;
fn ping(&self) -> String;
}
"#,
)
.unwrap();
write_crate_manifest(
&root,
"agent-impl",
&format!(
r#"
[dependencies]
agent-api = {{ path = "../agent-api" }}
golem-rust = {{ path = {}, features = ["export_golem_agentic"] }}
"#,
toml_string(golem_rust_path)
),
);
fs::write(
root.join("agent-impl/src/lib.rs"),
r#"
use agent_api::CrossCrateAgent;
use golem_rust::agent_implementation;
pub struct CrossCrateAgentImpl {
id: String,
}
#[agent_implementation]
impl CrossCrateAgent for CrossCrateAgentImpl {
fn new(id: String) -> Self {
Self { id }
}
fn ping(&self) -> String {
self.id.clone()
}
}
"#,
)
.unwrap();
write_crate_manifest(
&root,
"agent-client",
r#"
[dependencies]
agent-api = { path = "../agent-api" }
"#,
);
fs::write(
root.join("agent-client/src/lib.rs"),
r#"
use agent_api::CrossCrateAgentClient;
pub fn generated_client_type_is_available() -> usize {
std::mem::size_of::<CrossCrateAgentClient>()
}
"#,
)
.unwrap();
root
}
fn write_crate_manifest(root: &Path, name: &str, dependencies: &str) {
fs::write(
root.join(name).join("Cargo.toml"),
format!(
r#"
[package]
name = "{name}"
version = "0.0.0"
edition = "2024"
{dependencies}
"#
),
)
.unwrap();
}
fn toml_string(path: &Path) -> String {
format!("{:?}", path.display().to_string())
}
}