147 lines
4.7 KiB
Rust
147 lines
4.7 KiB
Rust
//! Generated native discovery, proc projection, and a real SDK import consumer.
|
|
use sausage::{Host, world::World};
|
|
use sausage_vfs::{Layer, Open};
|
|
use serde_json::{Value, json};
|
|
use std::{path::Path, time::Duration};
|
|
|
|
async fn call(host: &Host, method: &str, params: Value) -> Value {
|
|
// Same call budget as the core/watches host tests.
|
|
tokio::time::timeout(Duration::from_secs(20), host.call(method, params))
|
|
.await
|
|
.unwrap()
|
|
.unwrap()
|
|
}
|
|
|
|
async fn read(host: &Host, path: &str) -> Value {
|
|
let fs = host.filesystem().await.unwrap();
|
|
let file = fs.open(path, Open::default()).await.unwrap();
|
|
let bytes = file
|
|
.read(0, file.stat().await.unwrap().size as usize)
|
|
.await
|
|
.unwrap();
|
|
serde_json::from_slice(&bytes).unwrap()
|
|
}
|
|
|
|
/// No saved core.json: the RPC, proc file, importer and admission use generated types.
|
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
|
async fn generated_core_import_runs_under_wasi() {
|
|
let dir = tempfile::tempdir().unwrap();
|
|
let world = dir.path().join("world");
|
|
World::create(&world).unwrap();
|
|
let host = Host::start_with_cache(&world, Path::new(super::common::CACHE))
|
|
.await
|
|
.unwrap();
|
|
let doc = call(&host, "core.rpc.discover", json!({})).await;
|
|
assert_eq!(doc["openrpc"], "1.2.6");
|
|
assert_eq!(doc["info"]["title"], "core");
|
|
assert_eq!(read(&host, "/proc/core/discover.json").await, doc);
|
|
let fs = host.filesystem().await.unwrap();
|
|
assert!(
|
|
fs.list("/proc/core")
|
|
.await
|
|
.unwrap()
|
|
.iter()
|
|
.any(|e| e.name == "discover.json")
|
|
);
|
|
assert!(
|
|
fs.open(
|
|
"/proc/core/discover.json",
|
|
Open {
|
|
write: true,
|
|
..Open::default()
|
|
}
|
|
)
|
|
.await
|
|
.is_err()
|
|
);
|
|
let schemas = &doc["components"]["schemas"];
|
|
assert_eq!(schemas["Held"]["properties"]["held"]["type"], "boolean");
|
|
assert_eq!(schemas["Entry"]["properties"]["line"]["type"], "string");
|
|
let problems = sausage_interface::compat::check(&doc, &doc);
|
|
assert!(problems.is_empty(), "{problems:?}");
|
|
|
|
let source = dir.path().join("consumer");
|
|
std::fs::create_dir_all(source.join("imports")).unwrap();
|
|
std::fs::create_dir_all(source.join("src")).unwrap();
|
|
// This is exactly the user's copy-from-proc workflow, not a JSON fixture.
|
|
std::fs::write(source.join("imports/core.json"), doc.to_string()).unwrap();
|
|
let sdk = Path::new(env!("CARGO_MANIFEST_DIR"))
|
|
.parent()
|
|
.unwrap()
|
|
.join("sdk");
|
|
std::fs::write(
|
|
source.join("Cargo.toml"),
|
|
format!(
|
|
r#"
|
|
[package]
|
|
name = "native-core-consumer"
|
|
version = "0.1.0"
|
|
edition = "2024"
|
|
[workspace]
|
|
[dependencies]
|
|
sausage-plugin = {{ path = {sdk:?} }}
|
|
"#
|
|
),
|
|
)
|
|
.unwrap();
|
|
std::fs::write(
|
|
source.join("src/main.rs"),
|
|
r#"
|
|
use sausage_plugin::{Result, rpc, serde_json::json};
|
|
sausage_plugin::import!("core");
|
|
struct Consumer;
|
|
#[rpc]
|
|
impl Consumer {
|
|
/// Exercise actual generated parameter and result types.
|
|
async fn run(&self) -> Result<bool> {
|
|
let held = core::lock(core::Lock { path: "/typed-lock".into(), wait: Some(false) }).await?;
|
|
let filters: core::Log = sausage_plugin::serde_json::from_value(json!({"limit":1}))?;
|
|
for entry in core::log(filters).await? {
|
|
let _: String = entry.line;
|
|
}
|
|
core::unlock(core::Path { path: "/typed-lock".into() }).await?;
|
|
let doc = core::rpc_discover().await?;
|
|
Ok(held.held && doc["info"]["title"] == "core")
|
|
}
|
|
}
|
|
fn main() { sausage_plugin::run(Consumer) }
|
|
"#,
|
|
)
|
|
.unwrap();
|
|
let target = Path::new(concat!(env!("CARGO_TARGET_TMPDIR"), "/guests"));
|
|
assert!(
|
|
std::process::Command::new("cargo")
|
|
.args([
|
|
"build",
|
|
"--offline",
|
|
"--target",
|
|
"wasm32-wasip2",
|
|
"--manifest-path"
|
|
])
|
|
.arg(source.join("Cargo.toml"))
|
|
.arg("--target-dir")
|
|
.arg(target)
|
|
.status()
|
|
.unwrap()
|
|
.success()
|
|
);
|
|
std::fs::copy(
|
|
target.join("wasm32-wasip2/debug/native-core-consumer.wasm"),
|
|
world.join("candidate.wasm"),
|
|
)
|
|
.unwrap();
|
|
let activation = call(
|
|
&host,
|
|
"core.activate",
|
|
json!({"plugin":"consumer","wasm":"/candidate.wasm"}),
|
|
)
|
|
.await;
|
|
assert_eq!(activation["consumer"]["interface_check"], "passed");
|
|
assert_eq!(call(&host, "consumer.run", json!({})).await, true);
|
|
assert_eq!(
|
|
read(&host, "/proc/core/graph.json").await["plugins"]["consumer"]["imports"]["core"]["status"],
|
|
"compatible"
|
|
);
|
|
host.stop().await;
|
|
}
|