Mount the writable world root for every actor
ci / check (push) Successful in 3m9s

This commit is contained in:
Sausage Agent
2026-10-22 13:29:38 +00:00
parent 3d3f6f35fb
commit f932ab9719
6 changed files with 103 additions and 42 deletions
+51 -11
View File
@@ -140,20 +140,20 @@ async fn exit_request_is_answered_and_signals_the_host() {
}
#[tokio::test]
async fn filesystem_permissions_and_public_sharing() {
async fn filesystem_is_writable_and_public_aliases_share_files() {
let (_, host, mut mailbox) = filesystem_world("filesystem-permissions").await;
for path in [
"/actor/reader/public/denied.txt",
"/actor/writer/public/denied.txt",
"/actor/reader/public/shared.txt",
"/actor/writer/public/shared.txt",
] {
let denied = ask(
let written = ask(
&host,
&mut mailbox,
"writer",
json!({"type": "write", "path": path, "text": "denied"}),
json!({"type": "write", "path": path, "text": "shared"}),
)
.await;
assert_eq!(denied.body["type"], "error", "write to {path} succeeded");
assert_eq!(written.body["type"], "ok", "write to {path} failed");
}
let written = ask(
@@ -176,7 +176,7 @@ async fn filesystem_permissions_and_public_sharing() {
}
#[tokio::test]
async fn actors_can_read_but_not_write_other_actor_data() {
async fn actors_can_read_and_write_other_actor_data() {
let (_, host, mut mailbox) = filesystem_world("actor-data-sharing").await;
ask_type(
&host,
@@ -201,10 +201,10 @@ async fn actors_can_read_but_not_write_other_actor_data() {
&mut mailbox,
"reader",
json!({"type": "write", "path": "/actor/writer/data/x", "text": "changed"}),
"error",
"ok",
)
.await;
let unchanged = ask_type(
let changed = ask_type(
&host,
&mut mailbox,
"writer",
@@ -212,7 +212,47 @@ async fn actors_can_read_but_not_write_other_actor_data() {
"content",
)
.await;
assert_eq!(unchanged.body["text"], "shared");
assert_eq!(changed.body["text"], "changed");
host.stop().await;
}
#[tokio::test]
async fn world_root_is_shared_writable_and_survives_restart() {
let (world, host, mut mailbox) = filesystem_world("world-root").await;
assert!(world.root.join("config").is_dir());
let file = "/config/x";
ask_type(&host, &mut mailbox, "writer", json!({"type": "write", "path": file, "text": "first"}), "ok").await;
let read = json!({"type": "read", "path": file});
assert_eq!(ask_type(&host, &mut mailbox, "reader", read.clone(), "content").await.body["text"], "first");
ask_type(&host, &mut mailbox, "reader", json!({"type": "write", "path": file, "text": "second"}), "ok").await;
assert_eq!(std::fs::read_to_string(world.root.join("config/x")).unwrap(), "second");
for actor in ["writer", "reader"] {
assert_eq!(world.resolve(actor, file).unwrap(), world.root.join("config/x"));
}
assert_eq!(world.global_path(&world.root.join("config/x")).as_deref(), Some(file));
// Watches use the same world namespace, not only /actor paths.
let watching = request(&host, &mut mailbox, json!({"type": "watch", "path": world.root.join("config").display().to_string()})).await;
assert_eq!(watching.body, json!({"type": "watching", "path": "/config"}));
drain(&mut mailbox, Duration::from_millis(300)).await;
std::fs::write(world.root.join("config/event"), "watched").unwrap();
assert_eq!(fs_events(drain(&mut mailbox, Duration::from_millis(600)).await),
vec![json!({"type": "fs_changed", "path": "/config/event", "op": "create"})]);
request(&host, &mut mailbox, json!({"type": "unwatch", "path": world.root.join("config").display().to_string()})).await;
// A new top-level folder appears in already running instances, without a mount change.
std::fs::create_dir(world.root.join("docs")).unwrap();
std::fs::write(world.root.join("docs/new"), "host-created").unwrap();
let docs = json!({"type": "read", "path": "/docs/new"});
assert_eq!(ask_type(&host, &mut mailbox, "reader", docs, "content").await.body["text"], "host-created");
ask_type(&host, &mut mailbox, "reader", json!({"type": "write", "path": "/docs/new", "text": "actor-updated"}), "ok").await;
assert_eq!(std::fs::read_to_string(world.root.join("docs/new")).unwrap(), "actor-updated");
host.stop().await;
let (host, mut mailbox) = start(&world).await;
for actor in ["writer", "reader"] {
assert_eq!(ask_type(&host, &mut mailbox, actor, read.clone(), "content").await.body["text"], "second");
}
host.stop().await;
}
@@ -1187,7 +1227,7 @@ async fn a_watcher_overflow_sends_one_rescan_and_runs_a_full_pass() {
let report = host.broker.clone().overflowed().await;
assert!(report.iter().any(|r| r["type"] == "spawned" && r["actor"] == "planted"), "{report:?}");
let rescan = json!({"type": "fs_changed", "path": "/actor", "op": "rescan"});
let rescan = json!({"type": "fs_changed", "path": "/", "op": "rescan"});
assert_eq!(fs_events(drain(&mut mailbox, Duration::from_millis(600)).await), vec![rescan]);
assert!(bystander.try_recv().is_err(), "an actor without watches got the rescan");
host.stop().await;