Files
sausage/host/tests/p1.rs
T
surma ec6e20a9d7 host: sausage run with the userland bundle, real shell and services (T13)
- userland/bundle.nu builds the userland and assembles target/userland/bin
  (bin names, tools.txt aliases, brush as sh and bash).
- run and sh take --userland (default SAUSAGE_USERLAND); /bin is read-only.
- The console's bash tool runs through the process table with the
  conversation id; large output spills to the world's /tmp.
- sausage run starts the services supervisor.

P1 exit session (openai/gpt-6.1-sol, fresh world): write, read, edit, git
init/add/commit/diff/log, grep -rn, rg, find and a sed|sort|uniq|wc pipeline.
2026-11-06 17:34:11 +00:00

68 lines
2.4 KiB
Rust

//! P1 integration: a read-only bundle and real shell calls from the durable harness.
use sausage::{harness::run_module, world::World};
use sausage_vfs::Open;
use std::path::PathBuf;
#[tokio::test(flavor = "current_thread")]
async fn bundle_mount_and_real_shell() -> anyhow::Result<()> {
let repo = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("..");
let bundle = repo.join("target/userland/bin");
if !bundle.join("sh").exists() {
assert!(
std::process::Command::new("nu")
.env_remove("CARGO_TARGET_DIR")
.arg(repo.join("userland/bundle.nu"))
.status()?
.success()
);
}
let dir = tempfile::tempdir()?;
let world = World::open_with_userland(dir.path(), Some(&bundle))?;
assert!(world.root.stat("/bin/sh", true).await?.size > 0);
assert!(world.root.stat("/usr/local/bin", true).await?.directory);
let denied = world
.root
.open(
"/bin/forbidden",
Open {
write: true,
create: true,
..Default::default()
},
)
.await;
assert!(matches!(denied, Err(e) if e.kind() == std::io::ErrorKind::ReadOnlyFilesystem));
let exec = sausage::process::exec(World {
root: world.root.clone(),
host_dir: world.host_dir.clone(),
processes: world.processes.clone(),
})?;
let source = format!(
"globalThis.testP1 = true;\n{}",
include_str!("../js/test-bundle.js")
);
run_module(world, exec, &source).await?;
assert_eq!(
std::fs::read_to_string(dir.path().join("world/test.txt"))?,
"after\n"
);
assert_eq!(
std::fs::read_to_string(dir.path().join("world/parallel/a"))?,
"a"
);
assert_eq!(
std::fs::read_to_string(dir.path().join("world/parallel/b"))?,
"b"
);
let spills = std::fs::read_dir(dir.path().join("world/tmp"))?.collect::<Result<Vec<_>, _>>()?;
assert_eq!(spills.len(), 1);
let actual = std::fs::read_to_string(spills[0].path())?;
let expected: String = (1..=10000).map(|n| format!("{n}\n")).collect();
assert_eq!(actual, expected);
// An unbundled world remains usable by process tests and file-only callers.
let unbundled = tempfile::tempdir()?;
let world = World::open(unbundled.path())?;
assert!(world.root.stat("/bin/sh", true).await.is_err());
Ok(())
}