use super::*;
use crate::{Engine, remote_test_support::Server};
fn fixture() -> (Server, Engine, std::path::PathBuf) {
let server = Server::start();
let services = Arc::new(Services::with_state_directory(
server.dir.path().join("client"),
));
let mut engine = Engine::new();
engine.services = services.clone();
engine.runtime = Arc::new(Runtime::new(services, engine.documents.clone()));
let local = engine.runtime.services.local_connection().unwrap();
let remote = Arc::new(
RemoteServices::connect(
server.connect(),
local.identity.clone(),
WorkspaceLease {
client: local.client,
workspace: WorkspaceID([11; 16]),
lease: WorkspaceLeaseID([12; 16]),
runtime: RuntimeGeneration {
supervisor_boot: [0; 16],
container_start: None,
},
},
server.dir.path().join("client-spools"),
)
.unwrap(),
);
remote.set_authentication(None).unwrap();
engine.runtime.registry.lock().unwrap().remotes.insert(
"fixture".into(),
Route {
services: remote,
_owner: None,
locator: EnvironmentLocator {
route: RouteKind::Ssh,
ssh_profile: Some("buntrail-fixture.invalid".into()),
engine: None,
engine_context: None,
container_selector: None,
effective_user: None,
},
},
);
let file = server.dir.path().join("file");
(server, engine, file)
}
fn value(engine: &Engine, request: Value) -> Value {
let response = engine.call(request);
assert_eq!(response["ok"], true, "{response}");
response["value"].clone()
}
#[test]
fn framed_engine_round_trip_exceeds_legacy_frame_limit_and_preserves_encoding() {
let (_server, engine, path) = fixture();
let original = "x".repeat(9 * 1024 * 1024);
std::fs::write(&path, format!("\u{feff}{original}\r\n")).unwrap();
let read = value(
&engine,
json!({"command":"remote.call","id":"fixture","request":{"command":"fs.read","path":path}}),
);
assert_eq!(read["text"].as_str().unwrap().len(), original.len() + 2);
let doc = value(
&engine,
json!({"command":"document.open","text":read["text"],"version":read["version"],"path":path,"remote":"fixture"}),
);
value(
&engine,
json!({"command":"document.edit","id":doc["id"],"revision":0,"start":0,"end":1,"text":"🐇"}),
);
let saved = value(&engine, json!({"command":"document.save","id":doc["id"]}));
assert_eq!(saved["dirty"], false);
let actual = std::fs::read(&path).unwrap();
assert!(actual.starts_with(b"\xef\xbb\xbf"));
assert!(actual.ends_with(b"\r\n"));
assert!(String::from_utf8(actual).unwrap().contains('🐇'));
}
#[test]
fn retired_route_and_changed_target_never_fall_back_to_local_writes() {
let (_server, engine, path) = fixture();
std::fs::write(&path, "original").unwrap();
let read = value(
&engine,
json!({"command":"remote.call","id":"fixture","request":{"command":"fs.read","path":path}}),
);
let mut binding = Services::file_binding(read["version"].as_str().unwrap()).unwrap();
binding.file.target.effective_user.push(1);
binding.version.target = binding.file.target.clone();
let doc = value(
&engine,
json!({"command":"document.open","text":"original","path":path,"version":binding.token().unwrap(),"remote":"fixture"}),
);
value(
&engine,
json!({"command":"document.edit","id":doc["id"],"revision":0,"start":0,"end":8,"text":"unsafe"}),
);
assert_eq!(
engine.call(json!({"command":"document.save","id":doc["id"]}))["ok"],
false
);
engine
.runtime
.registry
.lock()
.unwrap()
.remotes
.remove("fixture");
assert_eq!(
engine.call(json!({"command":"document.save","id":doc["id"]}))["ok"],
false
);
assert_eq!(std::fs::read(&path).unwrap(), b"original");
}
#[test]
fn final_remote_close_waits_for_physical_retirement() {
use std::sync::atomic::{AtomicBool, Ordering};
let (_server, engine, _path) = fixture();
let engine = Arc::new(engine);
let started = Arc::new(AtomicBool::new(false));
let allowed = Arc::new(AtomicBool::new(false));
let mut registry = engine.runtime.registry.lock().unwrap();
let route = registry.remotes.get_mut("fixture").unwrap();
let mut service = (*route.services).clone();
let begin = started.clone();
let permit = allowed.clone();
service
.bind_retirement(Box::new(move || {
Box::pin(async move {
begin.store(true, Ordering::SeqCst);
while !permit.load(Ordering::SeqCst) {
std::thread::sleep(std::time::Duration::from_millis(2));
}
})
}))
.unwrap();
service.physical().unwrap().finish_fixture_startup();
route.services = Arc::new(service);
drop(registry);
let worker = engine.clone();
let (send, receive) = std::sync::mpsc::channel();
let thread = std::thread::spawn(move || {
let response = worker.call(json!({"command":"remote.close","id":"fixture"}));
send.send(response).unwrap();
});
let until = std::time::Instant::now() + std::time::Duration::from_secs(3);
while !started.load(Ordering::SeqCst) && std::time::Instant::now() < until {
std::thread::sleep(std::time::Duration::from_millis(2));
}
let early = receive.recv_timeout(std::time::Duration::from_millis(40));
allowed.store(true, Ordering::SeqCst);
let response = early.clone().unwrap_or_else(|_| {
receive
.recv_timeout(std::time::Duration::from_secs(3))
.unwrap()
});
thread.join().unwrap();
assert_eq!(response["ok"], true, "{response}");
assert!(
early.is_err(),
"Close published success before retiring its physical owner"
);
}
#[test]
fn handles_from_an_earlier_runtime_cannot_alias_a_new_environment() {
let mut previous = Registry::default();
let mut current = Registry::default();
assert_ne!(
previous.id("remote").unwrap(),
current.id("remote").unwrap(),
"Persisted recovery route handles must not authorize a later runtime"
);
}
#[test]
fn owned_terminal_layout_splits_reattaches_and_drains_without_relaunch() {
let (_server, engine, path) = fixture();
let root = path.parent().unwrap();
let first = value(
&engine,
json!({"command":"terminal.start","root":root,"shell":"/bin/sh"}),
);
let id = first["id"].clone();
let second = value(
&engine,
json!({"command":"terminal.start","root":root,"shell":"/bin/sh","split":id,"orientation":"Right"}),
);
assert_ne!(id, second["id"]);
let layout = value(&engine, json!({"command":"terminal.layout","root":root}));
assert_eq!(layout["tabs"].as_array().unwrap().len(), 1);
assert!(layout["tabs"][0]["root"].get("Split").is_some());
value(
&engine,
json!({"command":"terminal.write","id":id,"text":"printf 'owned-marker'; exit\n"}),
);
let mut bytes = Vec::new();
let deadline = std::time::Instant::now() + std::time::Duration::from_secs(10);
let mut sequence = 0;
loop {
let read = value(&engine, json!({"command":"terminal.read","id":id}));
bytes.extend(
read["bytes"]
.as_array()
.unwrap()
.iter()
.map(|b| b.as_u64().unwrap() as u8),
);
sequence = sequence.max(read["sequence"].as_u64().unwrap());
if read["drained"] == true {
break;
}
assert!(std::time::Instant::now() < deadline);
std::thread::sleep(std::time::Duration::from_millis(10));
}
assert!(String::from_utf8_lossy(&bytes).contains("owned-marker"));
value(
&engine,
json!({"command":"terminal.attach","id":id,"sequence":0}),
);
let mut replayed = Vec::new();
loop {
let replay = value(&engine, json!({"command":"terminal.read","id":id}));
replayed.extend(
replay["bytes"]
.as_array()
.unwrap()
.iter()
.map(|b| b.as_u64().unwrap() as u8),
);
assert!(replay["sequence"].as_u64().unwrap() <= sequence);
if replay["drained"] == true {
break;
}
assert!(std::time::Instant::now() < deadline);
}
assert_eq!(replayed, bytes);
value(&engine, json!({"command":"terminal.close","id":id}));
value(
&engine,
json!({"command":"terminal.close","id":second["id"]}),
);
}
#[test]
fn owned_remote_terminal_uses_verified_route_and_refuses_local_controls() {
let (_server, engine, path) = fixture();
let root = path.parent().unwrap();
let created = value(
&engine,
json!({"command":"remote.call","id":"fixture","request":{"command":"terminal.start","root":root,"shell":"/bin/sh"}}),
);
assert_eq!(
engine.call(json!({"command":"terminal.write","id":created["id"],"text":"exit\n"}))["ok"],
false
);
value(
&engine,
json!({"command":"remote.call","id":"fixture","request":{"command":"terminal.write","id":created["id"],"text":"printf REMOTE_OWNED; exit\n"}}),
);
let deadline = std::time::Instant::now() + std::time::Duration::from_secs(10);
let mut bytes = Vec::new();
loop {
let read = value(
&engine,
json!({"command":"remote.call","id":"fixture","request":{"command":"terminal.read","id":created["id"]}}),
);
bytes.extend(
read["bytes"]
.as_array()
.unwrap()
.iter()
.map(|n| n.as_u64().unwrap() as u8),
);
if read["drained"] == true {
break;
}
assert!(std::time::Instant::now() < deadline);
std::thread::sleep(std::time::Duration::from_millis(10));
}
assert!(String::from_utf8_lossy(&bytes).contains("REMOTE_OWNED"));
value(
&engine,
json!({"command":"remote.call","id":"fixture","request":{"command":"terminal.close","id":created["id"]}}),
);
}
#[test]
fn streaming_search_filters_and_cancel_use_bounded_jobs() {
let (_server, engine, path) = fixture();
std::fs::write(&path, "needle one\nneedle two\n").unwrap();
std::fs::write(path.with_extension("skip"), "needle hidden\n").unwrap();
let root = path.parent().unwrap();
let job = value(
&engine,
json!({"command":"service.start","request":{"command":"search.stream","root":root,"query":"needle","excludes":["*.skip"]}}),
);
let deadline = std::time::Instant::now() + std::time::Duration::from_secs(10);
let mut matches = 0;
let mut complete = false;
loop {
let poll = value(&engine, json!({"command":"service.poll","id":job["id"]}));
for e in poll["events"].as_array().unwrap() {
assert_ne!(e["kind"], "failed", "{e}");
if e["value"]["kind"] == "match" {
matches += 1;
assert_eq!(e["value"]["path"], json!(path));
}
if e["value"]["kind"] == "complete" {
complete = true;
}
}
if poll["done"] == true {
break;
}
assert!(std::time::Instant::now() < deadline);
std::thread::sleep(std::time::Duration::from_millis(10));
}
assert_eq!(matches, 2);
assert!(complete);
value(&engine, json!({"command":"service.release","id":job["id"]}));
}
#[test]
fn git_adapter_refuses_arbitrary_mutations_and_protects_registered_buffers() {
let (_server, engine, path) = fixture();
let root = path.parent().unwrap();
assert!(
std::process::Command::new("git")
.args(["init", "-q"])
.current_dir(root)
.status()
.unwrap()
.success()
);
let id = value(
&engine,
json!({"command":"document.open","text":"unsaved","path":path,"version":"legacy"}),
)["id"]
.clone();
assert_eq!(
engine.call(json!({"command":"git","root":root,"args":["checkout","--no-guess","other"]}))
["error"]["code"],
"busy"
);
assert_eq!(
engine.call(json!({"command":"git","root":root,"args":["clean","-fd"]}))["ok"],
false
);
value(
&engine,
json!({"command":"document.close","id":id,"discard":true}),
);
let status = value(
&engine,
json!({"command":"git","root":root,"args":["status","--short","--branch"]}),
);
assert_eq!(status["status"], 0);
}
#[test]
fn typed_git_round_trip_and_read_only_history_work_on_local_and_framed_routes() {
let (_server, engine, base) = fixture();
let root = base.parent().unwrap().join("repository");
std::fs::create_dir(&root).unwrap();
let path = root.join("file");
let git = |args: &[&str]| {
let result = std::process::Command::new("git")
.args(args)
.current_dir(&root)
.output()
.unwrap();
assert!(
result.status.success(),
"{}",
String::from_utf8_lossy(&result.stderr)
);
};
git(&["init", "-q"]);
git(&["config", "user.name", "Fixture"]);
git(&["config", "user.email", "fixture@example.invalid"]);
std::fs::write(&path, "one\n").unwrap();
for remote in [false, true] {
let call = |args: Value| {
let r = json!({"command":"git","root":root,"args":args});
let response = value(
&engine,
if remote {
json!({"command":"remote.call","id":"fixture","request":r})
} else {
r
},
);
if !response["operation"].is_null() {
let inspect = json!({"command":"git.inspect","operation":response["operation"]});
value(
&engine,
if remote {
json!({"command":"remote.call","id":"fixture","request":inspect})
} else {
inspect
},
);
}
response
};
let staged = call(json!(["add", "--", "file"]));
assert_eq!(staged["status"], 0, "{staged}");
assert_eq!(
call(json!([
"commit",
"-m",
if remote { "second" } else { "first" }
]))["status"],
0
);
let log = call(json!([
"log",
"--graph",
"--oneline",
"--decorate",
"--all",
"-100"
]));
assert_eq!(log["status"], 0);
assert!(log["stdout"].as_str().unwrap().contains("first"));
if !remote {
git(&["branch", "branch-fixture"]);
assert_eq!(
call(json!(["checkout", "--no-guess", "branch-fixture"]))["status"],
0
);
std::fs::write(&path, "two\n").unwrap();
}
}
assert!(
value(&engine, json!({"command":"git.pending"}))["entries"]
.as_array()
.unwrap()
.is_empty()
);
}
#[test]
fn git_proof_refusal_before_execution_does_not_leave_an_uninspectable_operation() {
let (_server, engine, path) = fixture();
let root = path.parent().unwrap();
for args in [
vec!["init", "-q"],
vec!["config", "user.name", "Fixture"],
vec!["config", "user.email", "fixture@example.invalid"],
] {
assert!(
std::process::Command::new("git")
.args(args)
.current_dir(root)
.status()
.unwrap()
.success()
);
}
std::fs::write(&path, "one").unwrap();
for args in [
vec!["add", "file"],
vec!["commit", "-qm", "initial"],
vec!["branch", "other"],
] {
assert!(
std::process::Command::new("git")
.args(args)
.current_dir(root)
.status()
.unwrap()
.success()
);
}
let result =
engine.call(json!({"command":"git","root":root,"args":["checkout","--no-guess","other"]}));
assert_eq!(result["ok"], false, "{result}");
assert!(
value(&engine, json!({"command":"git.pending"}))["entries"]
.as_array()
.unwrap()
.is_empty()
);
}
#[test]
fn confirmed_git_outcome_survives_a_dropped_reply_until_consumer_inspects() {
let (_server, engine, base) = fixture();
let root = base.parent().unwrap().join("repo");
std::fs::create_dir(&root).unwrap();
assert!(
std::process::Command::new("git")
.args(["init", "-q"])
.current_dir(&root)
.status()
.unwrap()
.success()
);
std::fs::write(root.join("file"), "safe").unwrap();
let result = value(
&engine,
json!({"command":"git","root":root,"args":["add","--","file"]}),
);
assert_eq!(result["status"], 0);
let pending = value(&engine, json!({"command":"git.pending"}));
assert_eq!(
pending["entries"].as_array().unwrap().len(),
1,
"A cancelled/stale consumer must still find the original outcome"
);
let inspected = value(
&engine,
json!({"command":"git.inspect","operation":result["operation"]}),
);
assert_eq!(inspected["resolved"], true);
assert!(
value(&engine, json!({"command":"git.pending"}))["entries"]
.as_array()
.unwrap()
.is_empty()
);
}
#[test]
fn recovered_codec_is_preserved_without_granting_a_live_file_binding() {
let (_server, engine, path) = fixture();
std::fs::write(&path, [255, 254, b'x', 0, 13, 0, 10, 0]).unwrap();
let read = value(&engine, json!({"command":"fs.read","path":path}));
let recovered = value(
&engine,
json!({"command":"document.open","text":"","encoding_source":read["version"]}),
);
assert!(recovered["path"].is_null() && recovered["version"].is_null());
assert_eq!(recovered["encoding_source"], read["version"]);
value(
&engine,
json!({"command":"document.edit","id":recovered["id"],"revision":0,"start":0,"end":0,"text":"kept\r\n"}),
);
let destination = path.with_extension("restored");
value(
&engine,
json!({"command":"fs.create","path":destination,"text":"kept\r\n","source_version":recovered["encoding_source"]}),
);
assert_eq!(
std::fs::read(destination).unwrap(),
[255, 254, b'k', 0, b'e', 0, b'p', 0, b't', 0, 13, 0, 10, 0]
);
}
#[test]
fn unreachable_remote_detachment_retires_local_authority_without_replay() {
let (mut server, engine, _) = fixture();
server.child.kill().unwrap();
server.child.wait().unwrap();
let start = std::time::Instant::now();
let detached = value(&engine, json!({"command":"remote.detach","id":"fixture"}));
assert_eq!(detached["confirmed"], false);
assert!(start.elapsed() < std::time::Duration::from_secs(2));
assert!(engine.runtime.route("fixture").is_err());
assert_eq!(
engine.call(json!({"command":"remote.detach","id":"fixture"}))["ok"],
false
);
}
#[test]
fn workspace_state_uses_persistent_ids_and_fences_missing_roots_and_stale_viewports() {
let (_server, engine, base) = fixture();
let root = base.parent().unwrap().join("workspace");
std::fs::create_dir(&root).unwrap();
let path = root.join("file");
std::fs::write(&path, "one\n").unwrap();
let opened = value(&engine, json!({"command":"workspace.open","root":root}));
assert_eq!(opened["state"], "Ready");
assert!(opened["worktree"].is_null());
let read = value(&engine, json!({"command":"fs.read","path":path}));
let doc = value(
&engine,
json!({"command":"document.open","text":read["text"],"path":path,"version":read["version"]}),
);
let viewport = json!({"selection":{"revision":999,"start":0,"end":1},"horizontal_offset":0.0,"vertical_offset":10.0,"folded_ranges":[],"revision_anchor":999});
let saved = value(
&engine,
json!({"command":"workspace.snapshot","root":root,"documents":[{"id":doc["id"],"viewport":viewport}],"selected":doc["id"]}),
);
let record = &saved["record"];
assert_eq!(record["documents"].as_array().unwrap().len(), 1);
assert!(record["documents"][0]["viewport"].is_null());
let persistent = record["documents"][0]["id"].clone();
assert_eq!(persistent.as_array().unwrap().len(), 16);
value(
&engine,
json!({"command":"document.close","id":doc["id"],"discard":false}),
);
let fresh = value(
&engine,
json!({"command":"document.open","text":read["text"],"path":path,"version":read["version"],"persistent_id":persistent}),
);
assert_ne!(fresh["id"], doc["id"]);
assert_eq!(fresh["persistent_id"], persistent);
value(
&engine,
json!({"command":"document.close","id":fresh["id"],"discard":false}),
);
std::fs::remove_dir_all(&root).unwrap();
let missing = value(
&engine,
json!({"command":"workspace.open","workspace":opened["record"]["id"]}),
);
assert_eq!(missing["state"], "Missing");
assert_eq!(missing["record"]["id"], opened["record"]["id"]);
let mut changed = opened["record"]["target"].clone();
changed["effective_user"] = json!([99]);
assert_eq!(
engine.call(json!({"command":"workspace.open","root":root,"expected":changed}))["ok"],
false
);
}
#[test]
fn verified_document_reconnect_retains_the_original_pending_save() {
let (_server, engine, path) = fixture();
std::fs::write(&path, "original").unwrap();
let read = value(
&engine,
json!({"command":"remote.call","id":"fixture","request":{"command":"fs.read","path":path}}),
);
let doc = value(
&engine,
json!({"command":"document.open","text":read["text"],"path":path,"version":read["version"],"remote":"fixture"}),
);
value(
&engine,
json!({"command":"document.edit","id":doc["id"],"revision":0,"start":0,"end":8,"text":"pending"}),
);
assert!(
engine
.save_with(doc["id"].as_u64().unwrap(), |_, _| Err(fail(
"save_uncertain",
"lost"
)))
.is_err()
);
let before = value(&engine, json!({"command":"document.status","id":doc["id"]}));
let remote = engine.runtime.route("fixture").unwrap();
let locator = engine.runtime.registry.lock().unwrap().remotes["fixture"]
.locator
.clone();
engine.runtime.registry.lock().unwrap().remotes.insert(
"resumed".into(),
Route {
services: remote,
_owner: None,
locator,
},
);
let resumed = value(
&engine,
json!({"command":"document.reconnect","id":doc["id"],"expected_remote":"fixture","remote":"resumed"}),
);
assert_eq!(resumed["remote"], "resumed");
assert_eq!(resumed["pending_operation"], before["pending_operation"]);
assert_eq!(resumed["text"], "pending");
assert_eq!(
engine.call(json!({"command":"document.save","id":doc["id"]}))["ok"],
false
);
}
#[test]
fn silent_remote_detachment_has_a_bounded_unconfirmed_result() {
let (server, engine, _) = fixture();
assert_eq!(
unsafe { libc::kill(server.child.id() as i32, libc::SIGSTOP) },
0
);
let start = std::time::Instant::now();
let detached = value(&engine, json!({"command":"remote.detach","id":"fixture"}));
assert_eq!(
unsafe { libc::kill(server.child.id() as i32, libc::SIGCONT) },
0
);
assert_eq!(detached["confirmed"], false);
assert!(start.elapsed() < std::time::Duration::from_secs(2));
assert!(engine.runtime.route("fixture").is_err());
}
#[test]
fn terminal_layout_is_memory_only_and_restart_is_empty() {
let root = tempfile::tempdir().unwrap();
let state = tempfile::tempdir().unwrap();
let make = || {
let services = Arc::new(Services::with_state_directory(state.path().to_owned()));
let mut engine = Engine::new();
engine.services = services.clone();
engine.runtime = Arc::new(Runtime::new(services, engine.documents.clone()));
engine
};
let engine = make();
let started = value(
&engine,
json!({"command":"terminal.start","root":root.path(),"shell":"/bin/sh"}),
);
let before = value(
&engine,
json!({"command":"terminal.layout","root":root.path()}),
);
value(
&engine,
json!({"command":"workspace.snapshot","root":root.path(),"documents":[]}),
);
let after = value(
&engine,
json!({"command":"terminal.layout","root":root.path()}),
);
assert_eq!(before["tabs"], after["tabs"]);
assert!(
engine
.runtime
.services
.local_connection()
.unwrap()
.store
.load()
.unwrap()
.records[0]
.terminal_layout
.tabs
.is_empty()
);
value(&engine, json!({"command":"workspace.shutdown"}));
drop(engine);
let engine = make();
let layout = value(
&engine,
json!({"command":"terminal.layout","root":root.path()}),
);
assert!(layout["tabs"].as_array().unwrap().is_empty());
assert_eq!(
engine.call(json!({"command":"terminal.attach","id":started["id"],"sequence":0}))["ok"],
false
);
}
#[test]
fn workspace_preferences_are_validated_before_persistence() {
let (_server, engine, base) = fixture();
let root = base.parent().unwrap().join("prefs");
std::fs::create_dir(&root).unwrap();
value(&engine, json!({"command":"workspace.open","root":root}));
let initial = value(&engine, json!({"command":"preferences.get","root":root}));
let mut style = initial["terminal"].clone();
style["font_size"] = json!(0);
assert_eq!(
engine.call(json!({"command":"preferences.set","root":root,"override":{"terminal":style}}))
["ok"],
false
);
assert_eq!(
value(&engine, json!({"command":"preferences.get","root":root})),
initial
);
style["font_size"] = json!(15);
assert_eq!(
value(
&engine,
json!({"command":"preferences.set","root":root,"override":{"terminal":style}})
)["terminal"]["font_size"],
15.0
);
}
#[test]
fn reopening_a_ready_saved_remote_reuses_its_registration_and_cancel_is_harmless() {
let (_server, engine, path) = fixture();
let opened = value(
&engine,
json!({"command":"remote.call","id":"fixture","request":{"command":"workspace.open","root":path.parent().unwrap()}}),
);
let mut wrong = opened["record"]["target"].clone();
wrong["effective_user"] = json!([99]);
assert_eq!(
engine.call(
json!({"command":"remote.begin","workspace":opened["record"]["id"],"expected":wrong})
)["ok"],
false
);
let begin = value(
&engine,
json!({"command":"remote.begin","workspace":opened["record"]["id"]}),
);
assert_eq!(begin["id"], "fixture");
let ready = value(&engine, json!({"command":"remote.poll","id":begin["id"]}));
assert_eq!(ready["reused"], true);
value(&engine, json!({"command":"remote.cancel","id":begin["id"]}));
assert!(
engine
.runtime
.route("fixture")
.unwrap()
.validate_environment_ready()
.is_ok()
);
}
#[test]
fn shutdown_does_not_initialize_or_require_an_unused_session_store() {
let state = tempfile::tempdir().unwrap();
std::fs::write(state.path().join("context"), "unavailable context").unwrap();
let services = Arc::new(Services::with_state_directory(state.path().to_owned()));
let mut engine = Engine::new();
engine.services = services.clone();
engine.runtime = Arc::new(Runtime::new(services, engine.documents.clone()));
assert_eq!(
value(&engine, json!({"command":"workspace.shutdown"}))["closed"],
true
);
assert_eq!(
std::fs::read(state.path().join("context")).unwrap(),
b"unavailable context"
);
}
#[test]
fn explorer_remove_is_nonrecursive_on_local_and_framed_routes() {
let (_server, engine, path) = fixture();
for remote in [false, true] {
let root = path.with_extension(if remote { "remote-dir" } else { "local-dir" });
std::fs::create_dir(&root).unwrap();
let child = root.join("keep");
std::fs::write(&child, "unsaved work").unwrap();
let remove = json!({"command":"fs.remove", "path":root});
let response = engine.call(if remote {
json!({"command":"remote.call", "id":"fixture", "request":remove})
} else {
remove
});
assert_eq!(response["ok"], false, "{response}");
assert_eq!(std::fs::read_to_string(&child).unwrap(), "unsaved work");
std::fs::remove_file(&child).unwrap();
let remove = json!({"command":"fs.remove", "path":root});
let response = engine.call(if remote {
json!({"command":"remote.call", "id":"fixture", "request":remove})
} else {
remove
});
assert_eq!(response["ok"], true, "{response}");
assert!(!root.exists());
}
}
#[test]
fn disconnected_terminal_context_survives_verified_route_replacement() {
let (server, engine, path) = fixture();
let old = engine.runtime.route("fixture").unwrap();
let owner = old.lease().clone();
let target = old.target().clone();
let locator = engine.runtime.registry.lock().unwrap().remotes["fixture"]
.locator
.clone();
let root = path.parent().unwrap();
let started = value(
&engine,
json!({"command":"remote.call","id":"fixture","request":{"command":"terminal.start","root":root,"shell":"/bin/sh"}}),
);
value(&engine, json!({"command":"remote.detach","id":"fixture"}));
assert_eq!(engine.call(json!({"command":"remote.call","id":"fixture","request":{"command":"terminal.write","id":started["id"],"text":"unsafe"}}))["ok"], false);
drop(old);
let remote = Arc::new(
RemoteServices::connect(
server.connect(),
target,
owner,
server.dir.path().join("new-spools"),
)
.unwrap(),
);
remote.set_authentication(None).unwrap();
engine.runtime.registry.lock().unwrap().remotes.insert(
"replacement".into(),
Route {
services: remote,
_owner: None,
locator,
},
);
let layout = value(
&engine,
json!({"command":"remote.call","id":"replacement","request":{"command":"terminal.layout","root":root}}),
);
assert_eq!(layout["tabs"], started["layout"]["tabs"]);
value(
&engine,
json!({"command":"remote.call","id":"replacement","request":{"command":"terminal.attach","id":started["id"],"sequence":0}}),
);
value(
&engine,
json!({"command":"remote.call","id":"replacement","request":{"command":"terminal.close","id":started["id"]}}),
);
}
#[test]
fn repository_browsing_and_prepared_actions_cross_framed_routes() {
let (_server, engine, base) = fixture();
let root = base.parent().unwrap().join("repository-new");
std::fs::create_dir(&root).unwrap();
for args in [
vec!["init", "-b", "main"],
vec!["config", "user.name", "Fixture"],
vec!["config", "user.email", "fixture@example.invalid"],
vec!["config", "commit.gpgsign", "false"],
vec!["config", "core.hooksPath", ".git/hooks"],
] {
assert!(
std::process::Command::new("git")
.args(args)
.current_dir(&root)
.status()
.unwrap()
.success()
);
}
std::fs::write(root.join("file"), b"one\r\n").unwrap();
for args in [
vec!["add", "file"],
vec!["commit", "-m", "repository fixture"],
] {
assert!(
std::process::Command::new("git")
.args(args)
.current_dir(&root)
.status()
.unwrap()
.success()
);
}
for remote in [false, true] {
let call = |request: Value| {
value(
&engine,
if remote {
json!({"command":"remote.call","id":"fixture","request":request})
} else {
request
},
)
};
let snapshot = call(json!({"command":"git.repository","root":root}));
let head = &snapshot["state"]["head"]["Branch"];
let oid = head["oid"].as_str().unwrap();
let name = if remote {
"remote-topic"
} else {
"local-topic"
};
let expected = json!({"Git":{"state":snapshot["state"],"worktree":snapshot["worktree"]}});
let create = call(
json!({"command":"git.mutate","root":root,"expected":expected,"action":{"CreateBranch":{"name":name.as_bytes(),"source_ref":head["name"],"start_oid":oid}}}),
);
assert_eq!(create["status"], 0, "{create}");
assert_eq!(
call(json!({"command":"git.inspect","operation":create["operation"]}))["resolved"],
true
);
let history = call(
json!({"command":"git.history","root":root,"filter":{"authored_since":null,"text":"REPOSITORY FIXTURE"}}),
);
assert_eq!(history["commits"].as_array().unwrap().len(), 1);
let detail = call(json!({"command":"git.commit","root":root,"oid":oid}));
let path = &detail["files"][0]["path"];
let diff = call(json!({"command":"git.commit_diff","root":root,"oid":oid,"path":path}));
assert_eq!(diff["content"], "Text");
let switch = call(
json!({"command":"git.mutate","root":root,"expected":expected,"action":{"SwitchBranch":{"name":name.as_bytes(),"expected_oid":oid}}}),
);
assert_eq!(switch["status"], 0, "{switch}");
assert_eq!(
call(json!({"command":"git.inspect","operation":switch["operation"]}))["resolved"],
true
);
let updated = call(json!({"command":"git.repository","root":root}));
assert_eq!(
updated["state"]["head"]["Branch"]["name"],
json!(format!("refs/heads/{name}").as_bytes())
);
assert_eq!(std::fs::read(root.join("file")).unwrap(), b"one\r\n");
}
}
#[test]
fn terminal_start_remote_directory_retains_authority_and_shell_cwd() {
let (_server, engine, path) = fixture();
let root = path.parent().unwrap();
let directory = root.join("nested folder/日本語");
std::fs::create_dir_all(&directory).unwrap();
let request =
json!({"command":"terminal.start","root":root,"directory":directory,"shell":"/bin/sh"});
let workspace = engine
.runtime
.terminal_workspace(Some("fixture"), &request)
.unwrap();
let (before, owner) = {
let w = workspace.lock().unwrap();
(w.record.clone(), w.owner.clone())
};
let created = value(
&engine,
json!({"command":"remote.call","id":"fixture","request":request}),
);
{
let w = workspace.lock().unwrap();
assert_eq!(w.record.id, before.id);
assert_eq!(w.record.locator, before.locator);
assert_eq!(w.record.target, before.target);
assert_eq!(w.owner, owner);
assert_eq!(
w.record.terminal_starts[0].directory,
TargetPath::posix(directory.to_str().unwrap().as_bytes().to_vec()).unwrap()
);
}
value(
&engine,
json!({"command":"remote.call","id":"fixture","request":{"command":"terminal.write","id":created["id"],"text":"printf '\\nCWD='; /bin/pwd; exit\n"}}),
);
let deadline = std::time::Instant::now() + std::time::Duration::from_secs(10);
let mut bytes = Vec::new();
loop {
let read = value(
&engine,
json!({"command":"remote.call","id":"fixture","request":{"command":"terminal.read","id":created["id"]}}),
);
bytes.extend(
read["bytes"]
.as_array()
.unwrap()
.iter()
.map(|n| n.as_u64().unwrap() as u8),
);
if read["drained"] == true {
break;
}
assert!(std::time::Instant::now() < deadline);
std::thread::sleep(std::time::Duration::from_millis(10));
}
let expected = std::fs::canonicalize(directory).unwrap();
assert!(
String::from_utf8_lossy(&bytes).contains(&format!("CWD={}\r\n", expected.display())),
"{}",
String::from_utf8_lossy(&bytes)
);
value(
&engine,
json!({"command":"remote.call","id":"fixture","request":{"command":"terminal.close","id":created["id"]}}),
);
}
#[test]
fn explorer_import_preserves_bytes_and_refuses_collisions() {
let dir = tempfile::tempdir().unwrap();
let services = Arc::new(Services::with_state_directory(dir.path().join("state")));
let mut engine = Engine::new();
engine.services = services.clone();
engine.runtime = Arc::new(Runtime::new(services, engine.documents.clone()));
let source = dir.path().join("source");
let destination = dir.path().join("copy");
let bytes = b"\xff\xfea\0\r\0\n\0\0\0";
std::fs::write(&source, bytes).unwrap();
let observed = value(&engine, json!({"command":"fs.observe","path":source}));
let request = json!({"command":"fs.import","root":dir.path(),"source_path":source,"source_observed":observed["observed"],"path":destination,"operation":OperationID([91;16])});
let imported = value(&engine, request.clone());
assert!(imported.get("receipt").is_some());
assert_eq!(std::fs::read(&destination).unwrap(), bytes);
value(
&engine,
json!({"command":"fs.inspect_operation","operation":OperationID([91;16])}),
);
let mut collision = request.clone();
collision["operation"] = json!(OperationID([92; 16]));
let collision = value(&engine, collision);
assert!(collision["receipt"]["state"].get("Failed").is_some());
assert_eq!(std::fs::read(&destination).unwrap(), bytes);
assert_eq!(std::fs::read(&source).unwrap(), bytes);
assert_eq!(
value(&engine, json!({"command":"fs.pending"}))["entries"],
json!([])
);
}
#[test]
fn explorer_git_multi_path_is_literal_and_unstages_unborn_index() {
let dir = tempfile::tempdir().unwrap();
assert!(
std::process::Command::new("git")
.args(["init", "--quiet"])
.arg(dir.path())
.status()
.unwrap()
.success()
);
let state = tempfile::tempdir().unwrap();
let services = Arc::new(Services::with_state_directory(state.path().to_owned()));
let mut engine = Engine::new();
engine.services = services.clone();
engine.runtime = Arc::new(Runtime::new(services, engine.documents.clone()));
for name in ["a[1]", "a1", "b"] {
std::fs::write(dir.path().join(name), name).unwrap();
}
let stage = value(
&engine,
json!({"command":"git","root":dir.path(),"args":["add","--","a[1]","b"]}),
);
assert_eq!(stage["status"], 0, "{stage}");
let staged = std::process::Command::new("git")
.arg("-C")
.arg(dir.path())
.args(["diff", "--cached", "--name-only", "-z"])
.output()
.unwrap();
assert_eq!(staged.stdout, b"a[1]\0b\0");
value(
&engine,
json!({"command":"git.inspect","operation":stage["operation"]}),
);
let unstage = value(
&engine,
json!({"command":"git","root":dir.path(),"args":["reset","HEAD","--","a[1]","b"]}),
);
assert_eq!(unstage["status"], 0, "{unstage}");
assert_eq!(std::fs::read(dir.path().join("a[1]")).unwrap(), b"a[1]");
}
#[test]
fn explorer_import_remote_keeps_source_and_never_falls_back_to_local() {
let (server, engine, path) = fixture();
let source = server.dir.path().join("source-binary");
let bytes = vec![0xff; 9 * 1024 * 1024];
std::fs::write(&source, &bytes).unwrap();
let observed = value(&engine, json!({"command":"fs.observe","path":source}));
let request = json!({"command":"fs.import","root":server.dir.path(),"source_path":source,"source_observed":observed["observed"],"path":path,"remote":"fixture","operation":OperationID([93;16])});
let reply = value(&engine, request.clone());
assert!(
reply["receipt"]["state"].get("Confirmed").is_some(),
"{reply}"
);
assert_eq!(std::fs::read(&path).unwrap(), bytes);
assert_eq!(std::fs::read(&source).unwrap(), bytes);
engine
.runtime
.registry
.lock()
.unwrap()
.remotes
.remove("fixture");
let rejected = server.dir.path().join("must-not-exist");
let mut request = request;
request["path"] = json!(rejected);
request["operation"] = json!(OperationID([94; 16]));
assert_eq!(engine.call(request)["ok"], false);
assert!(!rejected.exists());
}
#[test]
fn explorer_operations_retain_unknown_inspect_original_and_never_replay() {
use buntrail_core::workspaces::{FileRecoveryKind, FileRecoveryReference};
let dir = tempfile::tempdir().unwrap();
let state = tempfile::tempdir().unwrap();
let services = Arc::new(Services::with_state_directory(state.path().to_owned()));
let mut engine = Engine::new();
engine.services = services.clone();
engine.runtime = Arc::new(Runtime::new(services, engine.documents.clone()));
let path = dir.path().join("kept");
std::fs::write(&path, b"kept").unwrap();
let observed = value(&engine, json!({"command":"fs.observe","path":path}));
let binding = Services::file_binding(observed["observed"].as_str().unwrap()).unwrap();
let workspace = engine
.runtime
.terminal_workspace(None, &json!({"root":dir.path()}))
.unwrap();
let owner = workspace.lock().unwrap().owner.clone();
let local = engine.runtime.services.local_connection().unwrap();
let operation = OperationID([95; 16]);
local
.store
.retain_file_recovery(
owner.workspace,
FileRecoveryReference {
operation,
file: binding.file.clone(),
destination: None,
kind: FileRecoveryKind::Remove,
request_digest: [1; 32],
owner,
},
)
.unwrap();
let request = json!({"command":"fs.remove","root":dir.path(),"path":path,"observed":observed["observed"],"operation":operation});
assert_eq!(engine.call(request)["ok"], false);
assert_eq!(std::fs::read(&path).unwrap(), b"kept");
let unknown = value(
&engine,
json!({"command":"fs.inspect_operation","operation":operation}),
);
assert_eq!(unknown["resolved"], false);
assert_eq!(
value(&engine, json!({"command":"fs.pending"}))["entries"]
.as_array()
.unwrap()
.len(),
1
);
let receipt = run(local.files.mutate(FileMutationRequest {
operation,
file: binding.file,
expected: ExpectedPathState::Present(binding.version),
command: FileMutation::Remove,
}));
assert!(matches!(receipt.state, OperationState::Confirmed(_)));
assert_eq!(
value(
&engine,
json!({"command":"fs.inspect_operation","operation":operation})
)["resolved"],
true
);
assert_eq!(
value(&engine, json!({"command":"fs.pending"}))["entries"],
json!([])
);
}
#[test]
fn explorer_review_completed_move_stays_inspectable_until_acknowledged() {
let dir = tempfile::tempdir().unwrap();
let state = tempfile::tempdir().unwrap();
let services = Arc::new(Services::with_state_directory(state.path().to_owned()));
let mut engine = Engine::new();
engine.services = services.clone();
engine.runtime = Arc::new(Runtime::new(services, engine.documents.clone()));
let path = dir.path().join("before");
let to = dir.path().join("after");
std::fs::write(&path, b"kept").unwrap();
let observed = value(&engine, json!({"command":"fs.observe","path":path}));
let reply = value(
&engine,
json!({"command":"fs.rename","root":dir.path(),"path":path,"to":to,"observed":observed["observed"],"operation":OperationID([96;16])}),
);
assert!(reply["receipt"]["state"].get("Confirmed").is_some());
let pending = value(&engine, json!({"command":"fs.pending"}));
assert_eq!(
pending["entries"].as_array().unwrap().len(),
1,
"Discarded delivery must not lose its recovery reference"
);
let inspected = value(
&engine,
json!({"command":"fs.inspect_operation","operation":OperationID([96;16])}),
);
assert_eq!(inspected["resolved"], true);
assert_eq!(std::fs::read(to).unwrap(), b"kept");
}
#[test]
fn explorer_review_move_can_leave_workspace_without_overwriting() {
let dir = tempfile::tempdir().unwrap();
let state = tempfile::tempdir().unwrap();
let workspace = dir.path().join("project");
std::fs::create_dir(&workspace).unwrap();
let services = Arc::new(Services::with_state_directory(state.path().to_owned()));
let mut engine = Engine::new();
engine.services = services.clone();
engine.runtime = Arc::new(Runtime::new(services, engine.documents.clone()));
let path = workspace.join("before");
let to = dir.path().join("after");
std::fs::write(&path, b"kept").unwrap();
let observed = value(&engine, json!({"command":"fs.observe","path":path}));
let reply = value(
&engine,
json!({"command":"fs.rename","root":workspace,"path":path,"to":to,"observed":observed["observed"],"operation":OperationID([97;16])}),
);
assert!(reply["receipt"]["state"].get("Confirmed").is_some());
assert_eq!(std::fs::read(to).unwrap(), b"kept");
}
#[cfg(unix)]
#[test]
fn explorer_capabilities_resolves_aliased_workspace_root() {
let dir = tempfile::tempdir().unwrap();
let root = dir.path().join("project");
let alias = dir.path().join("alias");
std::fs::create_dir(&root).unwrap();
std::os::unix::fs::symlink(&root, &alias).unwrap();
let engine = Engine::new();
let reply = value(
&engine,
json!({"command":"fs.explorer_capabilities","root":alias}),
);
assert_eq!(
reply["root_path"],
std::fs::canonicalize(root).unwrap().to_str().unwrap()
);
}