//! 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 { 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; }