Generate native core discovery from typed dispatch contracts

This commit is contained in:
Sausage
2026-11-02 00:10:34 +00:00
parent ee88adf6c6
commit facaf1d9b1
15 changed files with 702 additions and 98 deletions
+146
View File
@@ -0,0 +1,146 @@
//! 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;
}