//! P1 integration: independent bundle copies 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_copy_and_real_shell() -> anyhow::Result<()> { let repo = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(".."); let bundle = PathBuf::from(std::env::var("CARGO_TARGET_DIR").unwrap()).join("bundle"); if !bundle.join("bin/sh.wasm").exists() { let _guard = crate::common::BUILD.lock().unwrap(); assert!( std::process::Command::new("nu") .arg(repo.join("userland/bundle.nu")) .arg(&bundle) .status()? .success() ); } let dir = tempfile::tempdir()?; let world = World::open_with_userland(dir.path(), Some(&bundle))?; assert!(world.root.stat("/bin/sh.wasm", true).await?.size > 0); use std::os::unix::fs::MetadataExt; for entry in std::fs::read_dir(dir.path().join("world/bin"))? { let entry = entry?; assert_eq!(entry.path().extension().unwrap(), "wasm"); assert_eq!(entry.metadata()?.nlink(), 1); assert!(!entry.file_type()?.is_symlink()); } assert!(world.root.stat("/usr/local/bin", true).await?.directory); let writable = world .root .open( "/bin/forbidden", Open { write: true, create: true, ..Default::default() }, ) .await; assert!(writable.is_ok()); let exec = sausage::process::exec(World { root: world.root.clone(), host_dir: world.host_dir.clone(), processes: world.processes.clone(), })?; use sausage::exec::{Command, Options, Stream}; for (script, expected) in [ ("command -v ls", "/bin/ls.wasm\n"), ("ls", "bin"), ("git --version", "git version"), ("printf 'a\\n' | xargs echo", "a\n"), ("env FOO=1 printenv FOO", "1\n"), ] { let mut out = Vec::new(); let code = exec .exec( Command::Shell(script.into()), Options::default(), &mut |stream, bytes| { if matches!(stream, Stream::Stdout) { out.extend_from_slice(bytes); } }, ) .await?; assert_eq!(code, 0, "{script}"); assert!(String::from_utf8(out)?.contains(expected), "{script}"); } let mut history = Vec::new(); assert_eq!( exec.exec( Command::Shell("cd /src/sausage && git log --oneline".into()), Options::default(), &mut |stream, bytes| { if matches!(stream, Stream::Stdout) { history.extend_from_slice(bytes); } }, ) .await?, 0, ); let history = String::from_utf8(history)?; assert_eq!(history.lines().count(), 1); assert!(history.contains("Bundle source ")); let mut status = Vec::new(); assert_eq!( exec.exec( Command::Shell("cd /src/sausage && git status --short".into()), Options::default(), &mut |stream, bytes| { if matches!(stream, Stream::Stdout) { status.extend_from_slice(bytes); } }, ) .await?, 0, ); assert!(status.is_empty(), "{}", String::from_utf8_lossy(&status)); let source = format!( "globalThis.testP1 = true;\n{}", include_str!("../../js/test-bundle.js") ); run_module(world, exec.clone(), &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::, _>>()?; 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); let source_cat = std::fs::read(bundle.join("bin/cat.wasm"))?; let head = std::fs::read(dir.path().join("world/bin/head.wasm"))?; let mut out = Vec::new(); assert_eq!( exec.exec( Command::Shell( "cp /bin/true.wasm /bin/cat.wasm; cat /test.txt; head -n 1 /test.txt".into() ), Options::default(), &mut |stream, bytes| { if matches!(stream, Stream::Stdout) { out.extend_from_slice(bytes); } } ) .await?, 0 ); assert_eq!(out, b"after\n"); assert_eq!(std::fs::read(bundle.join("bin/cat.wasm"))?, source_cat); assert_eq!(std::fs::read(dir.path().join("world/bin/head.wasm"))?, head); assert_eq!( exec.exec( Command::Shell("cp /bin/true.wasm /usr/local/bin/head.wasm; head /test.txt".into()), Options::default(), &mut |_, bytes| assert!(bytes.is_empty()) ) .await?, 0 ); let changed = std::fs::read(dir.path().join("world/bin/cat.wasm"))?; let reopened = World::open_with_userland( dir.path(), Some(std::path::Path::new("/nonexistent-bundle")), )?; assert_eq!( reopened .root .open("/bin/cat.wasm", Open::default()) .await? .read(0, changed.len()) .await?, changed ); // 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(()) }