133 lines
4.8 KiB
Rust
133 lines
4.8 KiB
Rust
//! Real guest mutations share tool-call tags and restore a committed repository.
|
|
use sausage::{
|
|
exec::{Command, Exec, Options, Stream},
|
|
process::Runtime,
|
|
world::World,
|
|
};
|
|
use sausage_vfs::journal::Outcome;
|
|
use std::{path::PathBuf, sync::Arc};
|
|
async fn run(runtime: &Arc<Runtime>, id: &str, argv: &[&str]) -> String {
|
|
let mut out = Vec::new();
|
|
let mut err = Vec::new();
|
|
let code = runtime
|
|
.exec(
|
|
Command::Argv(argv.iter().map(|s| s.to_string()).collect()),
|
|
Options {
|
|
cwd: "/repo".into(),
|
|
tool_call: Some(id.into()),
|
|
conversations: vec!["conversation".into()],
|
|
..Default::default()
|
|
},
|
|
&mut |stream, bytes| match stream {
|
|
Stream::Stdout => out.extend_from_slice(bytes),
|
|
Stream::Stderr => err.extend_from_slice(bytes),
|
|
},
|
|
)
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(code, 0, "{}", String::from_utf8_lossy(&err));
|
|
String::from_utf8(out).unwrap()
|
|
}
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn tool_call_commit_and_independent_change() {
|
|
let target = PathBuf::from(
|
|
std::env::var("CARGO_TARGET_DIR").expect("set the executor target directory"),
|
|
);
|
|
let guest_dir = target.join("wasm32-wasip2/release");
|
|
if !guest_dir.join("git.wasm").is_file()
|
|
|| !guest_dir.join("coreutils.wasm").is_file()
|
|
|| !guest_dir.join("undo.wasm").is_file()
|
|
{
|
|
crate::common::cwd_init();
|
|
assert!(
|
|
std::process::Command::new("cargo")
|
|
.args([
|
|
"build",
|
|
"--release",
|
|
"-p",
|
|
"git",
|
|
"-p",
|
|
"sausage-coreutils",
|
|
"-p",
|
|
"undo"
|
|
])
|
|
.current_dir(PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../userland"))
|
|
.status()
|
|
.unwrap()
|
|
.success()
|
|
);
|
|
}
|
|
let temp = tempfile::tempdir().unwrap();
|
|
let world = World::open(temp.path()).unwrap();
|
|
let root = temp.path().join("world");
|
|
std::fs::create_dir_all(root.join("bin")).unwrap();
|
|
std::fs::create_dir_all(root.join("repo")).unwrap();
|
|
for (binary, guest) in [
|
|
("git", "git"),
|
|
("coreutils", "cp"),
|
|
("coreutils", "touch"),
|
|
("undo", "undo"),
|
|
] {
|
|
std::fs::copy(
|
|
target.join(format!("wasm32-wasip2/release/{binary}.wasm")),
|
|
root.join(format!("bin/{guest}")),
|
|
)
|
|
.unwrap();
|
|
}
|
|
let git = |args: &[&str]| {
|
|
let output = std::process::Command::new("git")
|
|
.args(args)
|
|
.current_dir(root.join("repo"))
|
|
.output()
|
|
.unwrap();
|
|
assert!(
|
|
output.status.success(),
|
|
"{}",
|
|
String::from_utf8_lossy(&output.stderr)
|
|
);
|
|
String::from_utf8(output.stdout).unwrap()
|
|
};
|
|
git(&["init", "-b", "main"]);
|
|
git(&["config", "user.name", "Test"]);
|
|
git(&["config", "user.email", "test@example.com"]);
|
|
std::fs::write(root.join("repo/file"), "old\n").unwrap();
|
|
git(&["add", "file"]);
|
|
git(&["commit", "--no-gpg-sign", "-m", "initial"]);
|
|
let before = git(&["log", "--oneline"]);
|
|
std::fs::write(root.join("input"), "new\n").unwrap();
|
|
let runtime = Runtime::new(world).unwrap();
|
|
run(&runtime, "A", &["/bin/cp", "/input", "file"]).await;
|
|
run(&runtime, "A", &["/bin/git", "add", "file"]).await;
|
|
run(&runtime, "A", &["/bin/git", "commit", "-m", "change"]).await;
|
|
run(&runtime, "B", &["/bin/touch", "independent"]).await;
|
|
let entries = run(
|
|
&runtime,
|
|
"list",
|
|
&["/bin/undo", "list", "--conversation", "conversation"],
|
|
)
|
|
.await;
|
|
let entries: Vec<sausage_vfs::journal::Entry> = serde_json::from_str(&entries).unwrap();
|
|
assert!(entries.iter().any(|entry| entry.tag == "A"));
|
|
let snapshots = run(&runtime, "show", &["/bin/undo", "show", "A"]).await;
|
|
let snapshots: Vec<sausage_vfs::journal::Snapshot> = serde_json::from_str(&snapshots).unwrap();
|
|
assert!(
|
|
snapshots
|
|
.iter()
|
|
.any(|snapshot| snapshot.path == "/repo/file")
|
|
);
|
|
let output = run(&runtime, "undo-call", &["/bin/undo", "A"]).await;
|
|
let outcome: Outcome = serde_json::from_str(&output).unwrap();
|
|
assert!(outcome.conflicts.is_empty(), "{:?}", outcome);
|
|
assert!(outcome.tag.is_some());
|
|
assert_eq!(std::fs::read(root.join("repo/file")).unwrap(), b"old\n");
|
|
assert!(root.join("repo/independent").is_file());
|
|
assert_eq!(
|
|
run(&runtime, "inspect", &["/bin/git", "log", "--oneline"]).await,
|
|
before
|
|
);
|
|
assert_eq!(
|
|
run(&runtime, "inspect", &["/bin/git", "status", "--short"]).await,
|
|
"?? independent\n"
|
|
);
|
|
}
|