Files
sausage/cli/tests/cli.rs
T
surma d7b78ed0fd core: review fixes, init builds the agent, console without a terminal
Activation path (review T6R): a broken plugin or a stray file in
/plugins blocks no activation; candidates replace their own entries;
discovery has a 10 s deadline and its process is stopped after the
answer; an unchanged hash keeps plugin.prev.wasm; activations run in
their own task; the watcher is not recursive; no duplicate services;
rpc.retired waits for live processes of its version; reads and hashes
on blocking threads.

Guest I/O and per-call paths: a stdin write error is EOF, stdout and
stderr are drained before the status; stderr is lossy UTF-8 and goes
to the journal from its own reader; compiled components are freed;
shutdown starts no new processes; lock queues drop stopping processes;
compile before the admission gate; CommandPre per component;
core.unlock in order; null errors are successes; the journal thread
survives SQLite errors.

sausage init builds the agent with cargo.build; sausage without a
command creates ./world. The console reads plain lines without a
terminal. PROTOCOL.md and DECISIONS.md describe the decided rules. The
flake tests the whole workspace and checks the packaged cargo.build.
2026-10-23 02:25:03 +01:00

67 lines
2.5 KiB
Rust

//! Initialize the embedded agent and start the resulting world through the CLI.
use sausage::Host;
use serde_json::{Value, json};
use std::process::{Command, Stdio};
/// Environment configuration creates a complete world with a callable agent.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn init_builds_agent() {
let temporary = std::env::temp_dir().join(format!("sausage-cli-{}", uuid::Uuid::now_v7()));
std::fs::create_dir_all(&temporary).unwrap();
let world = temporary.join("world");
let output = Command::new(env!("CARGO_BIN_EXE_sausage"))
.arg("init")
.arg(&world)
.env("SAUSAGE_API", "anthropic-messages")
.env("SAUSAGE_BASE_URL", "http://127.0.0.1:1/")
.env("SAUSAGE_MODEL", "init-test")
.env("SAUSAGE_API_TOKEN", "fake-token")
.stdin(Stdio::null())
.output()
.unwrap();
assert!(
output.status.success(),
"stdout: {}\nstderr: {}",
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
);
let stdout = String::from_utf8(output.stdout).unwrap();
assert!(stdout.contains("first agent build takes a few minutes"));
assert!(stdout.contains("sausage run"));
let config: Value =
serde_json::from_slice(&std::fs::read(world.join("config/agent.json")).unwrap()).unwrap();
assert_eq!(
config,
json!({"api":"anthropic-messages","base_url":"http://127.0.0.1:1","model":"init-test","token":"fake-token"})
);
assert_eq!(
serde_json::from_slice::<Value>(&std::fs::read(world.join("config/boot.json")).unwrap())
.unwrap(),
json!([])
);
for file in [
"plugins/cargo/sdk/Cargo.toml",
"plugins/cargo/sdk/src/lib.rs",
"plugins/agent/source/Cargo.toml",
"plugins/agent/source/src/main.rs",
"plugins/agent/plugin.wasm",
] {
assert!(world.join(file).is_file(), "missing {file}");
}
let cache =
std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../target/sdk/cli-cache");
let host = Host::start_with_cache(&world, &cache).await.unwrap();
let doc = host.call("agent.rpc.discover", json!({})).await.unwrap();
assert_eq!(doc["x-sausage-grace"], 600);
assert!(
doc["methods"]
.as_array()
.unwrap()
.iter()
.any(|method| method["name"] == "chat")
);
host.stop().await;
std::fs::remove_dir_all(temporary).unwrap();
}