use super::*;
use crate::boundary::deposit;
use crate::test_support::world_under;
use crate::ui_state::Clock;
use serde_json::json;
use std::time::{Duration, Instant};
use tempfile::tempdir;
struct AtClock(i64);
impl Clock for AtClock {
fn now(&self) -> Instant {
Instant::now()
}
fn stamp(&self) -> String {
self.0.to_string()
}
}
#[test]
fn a_booted_engine_answers_a_deposit_and_stops_on_drop() {
let root = tempdir().unwrap();
let world = world_under(root.path());
let stale = world.yog_stage_root().join("nonce");
std::fs::create_dir_all(&stale).unwrap();
let far_future = 1_000_000_000_000;
let state_root = world.yog_state_root();
deposit::deposit(&state_root, "e-1", &json!({"op": "attention"})).unwrap();
let engine = Engine::boot(&world, &[], Arc::new(AtClock(far_future))).expect("booted");
assert!(!stale.exists(), "the §5.2 startup sweep is the engine's");
assert_eq!(
engine.model.ui_json_path(),
state_root.join("ui.json"),
"the model is rooted in the world it was booted into"
);
let deadline = Instant::now() + Duration::from_secs(10);
let reply = loop {
if let Some(reply) = deposit::read_reply(&state_root, "e-1") {
break reply;
}
assert!(Instant::now() < deadline, "the engine answered no deposit");
std::thread::sleep(Duration::from_millis(10));
};
assert_eq!(reply["ok"], true);
assert_eq!(reply["kind"], "attention", "the §6 queue, headless");
assert_eq!(
reply["rows"].as_array().map(Vec::len),
Some(0),
"an empty world asks nothing of anyone"
);
drop(engine); }
#[test]
fn a_boot_whose_stated_address_cannot_bind_refuses_and_consumes_nothing() {
let root = tempdir().unwrap();
let world = world_under(root.path());
let dir = crate::wire::material::dir(&world);
crate::test_support::wire::mint(&dir);
std::fs::write(
dir.join(crate::wire::material::ADDRESS),
"256.256.256.256:1\n",
)
.unwrap();
let state_root = world.yog_state_root();
deposit::deposit(&state_root, "e-2", &json!({"op": "attention"})).unwrap();
let refusal = Engine::boot(&world, &[], Arc::new(AtClock(0)))
.err()
.expect("a yog that cannot listen is not an engine");
assert!(refusal.starts_with("wire: "), "{refusal}");
assert!(
refusal.contains("256.256.256.256:1"),
"it names the address: {refusal}"
);
assert!(
deposit::read_reply(&state_root, "e-2").is_none(),
"a boot that refuses has not consumed a gesture"
);
}
#[test]
fn a_second_engine_on_one_world_refuses_while_the_first_keeps_answering() {
let root = tempdir().unwrap();
let world = world_under(root.path());
let state_root = world.yog_state_root();
let first = Engine::boot(&world, &[], Arc::new(AtClock(0))).expect("the first engine");
let refusal = Engine::boot(&world, &[], Arc::new(AtClock(0)))
.err()
.expect("the second is refused");
assert!(refusal.contains("one world has one engine"), "{refusal}");
deposit::deposit(&state_root, "e-3", &json!({"op": "attention"})).unwrap();
let deadline = Instant::now() + Duration::from_secs(10);
let reply = loop {
if let Some(reply) = deposit::read_reply(&state_root, "e-3") {
break reply;
}
assert!(Instant::now() < deadline, "the one engine still answers");
std::thread::sleep(Duration::from_millis(10));
};
assert_eq!(reply["ok"], true);
drop(first);
let deadline = Instant::now() + Duration::from_secs(5);
while crate::engine::sole::take(&state_root).is_err() {
assert!(Instant::now() < deadline, "the world never freed");
std::thread::sleep(Duration::from_millis(20));
}
}