From 3fac90076d426e605367590328d07ae333fefef4 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Sat, 10 Oct 2026 09:05:22 +0200 Subject: [PATCH] mind-body: the reader takes the instance only; the control root is a constant a caller cannot supply read_effective(instance) reads CONTROL_ROOT//control.cc. The root override is a thread-local under cfg(test) inside the crate; the reader tests and the Grant launch tests move in-crate to use it. The operator CLI's show derives its display line from the store it already read. An external forge probe and compile_fail examples pin the rule; the instance gate is pinned with same-length, prefix and extension forgeries, and the Persona refusal with a claim of every kind. Co-Authored-By: Claude Opus 5.5 --- crates/mind-body/src/cli.rs | 4 +- crates/mind-body/src/control.rs | 108 +++++-- crates/mind-body/src/control/tests.rs | 414 ++++++++++++++++++++++++++ crates/mind-body/src/launch.rs | 25 +- crates/mind-body/tests/control.rs | 254 +--------------- crates/mind-body/tests/forge.rs | 66 ++++ crates/mind-body/tests/grant.rs | 114 ------- 7 files changed, 577 insertions(+), 408 deletions(-) create mode 100644 crates/mind-body/src/control/tests.rs create mode 100644 crates/mind-body/tests/forge.rs delete mode 100644 crates/mind-body/tests/grant.rs diff --git a/crates/mind-body/src/cli.rs b/crates/mind-body/src/cli.rs index 4827bf7..bb2f4b2 100644 --- a/crates/mind-body/src/cli.rs +++ b/crates/mind-body/src/cli.rs @@ -18,7 +18,7 @@ use chrono::{DateTime, Utc}; use eureka_pipeline::Slug; use rust_decimal::Decimal; -use crate::control::{BurnRate, ControlWriter, FileSource, control_path, load_state, read_effective}; +use crate::control::{BurnRate, ControlWriter, FileSource, control_path, derive, load_state}; const USAGE: &str = "usage: mind-control [--root ] --instance show | brake hold | brake release | dial set --heat H --base-cooldown-s S --base-run-usd U"; @@ -120,6 +120,6 @@ fn show(root: &Path, instance: &Slug, out: &mut impl Write) -> Result<()> { } Err(error) => writeln!(out, "store: undecodable ({error:#})")?, } - writeln!(out, "effective: {:?}", read_effective(root, instance))?; + writeln!(out, "effective: {:?}", derive(&source, instance))?; Ok(()) } diff --git a/crates/mind-body/src/control.rs b/crates/mind-body/src/control.rs index 19727ef..bf4a65e 100644 --- a/crates/mind-body/src/control.rs +++ b/crates/mind-body/src/control.rs @@ -3,9 +3,11 @@ //! `Released` carries a `Grant`, the only value that hands a run cap to the //! launch: it cannot be built outside this module, so a cap exists only as the //! reading of a released brake and an in-bounds dial. The Grant names the -//! instance whose store was read, and that store's path is derived from the -//! instance here, by the one reader: no caller supplies a path or a source, so -//! a Grant is the proof that this instance's own root-owned store released. +//! instance whose store was read. The store is at a fixed place, `CONTROL_ROOT`, +//! and the one reader takes the instance only: no function, argument, variable +//! or flag lets a caller choose a root, a path or a source, so a Grant is the +//! proof that this instance's own root-owned store released. Only a +//! `#[cfg(test)]` override inside this crate moves the root. //! //! State is a CultCache single-file store at //! `/etc/gamecult/minds//control.cc`, root-owned and world-readable. @@ -35,8 +37,8 @@ use eureka_pipeline::Slug; use rust_decimal::Decimal; use rust_decimal::prelude::ToPrimitive; -/// Where every instance's control directory lives. Tests and the library never -/// touch it: the root is a parameter. +/// Where every instance's control directory lives. The reader reads only here; +/// the operator's CLI takes its own `--root` for writing and display. pub const CONTROL_ROOT: &str = "/etc/gamecult/minds"; /// The one key each control document has in the store. @@ -170,7 +172,7 @@ pub struct ControlState { impl ControlState { /// The one derivation. Order: brake first, then dial; each failing rung is /// its own reason. - fn effective(&self, instance: &Slug) -> Effective { + pub(crate) fn effective(&self, instance: &Slug) -> Effective { let held = |reason| Effective::Held { reason }; let Some(brake) = &self.brake else { return held(HeldReason::BrakeAbsent); @@ -233,45 +235,85 @@ pub fn load_state(source: &impl ControlSource) -> Result { } /// The only reader every organ uses (the waker now; the permit issuer and the -/// Persona organ later). The store is `instance`'s own, at the path -/// `control_path` derives under `root`; the caller supplies neither a path nor -/// a source. An instance that is not a slug reads as held. -pub fn read_effective(root: &Path, instance: &Slug) -> Effective { - match control_path(root, instance) { +/// Persona organ later). The store is `instance`'s own at +/// `CONTROL_ROOT//control.cc`; the caller supplies neither a root, a +/// path nor a source. An instance that is not a slug reads as held. +/// +/// Nothing outside this crate can name another place: +/// +/// ```compile_fail +/// use eureka_pipeline::Slug; +/// // a root is not an argument +/// let _ = mind_body::control::read_effective(std::path::Path::new("/tmp"), &Slug("eureka".into())); +/// ``` +/// +/// ```compile_fail +/// // there is no reader over a source of the caller's +/// let _ = mind_body::control::read_effective_from; +/// ``` +/// +/// ```compile_fail +/// use mind_body::control::Grant; +/// // a Grant cannot be written out +/// let _ = Grant { instance: eureka_pipeline::Slug("eureka".into()), cadence: std::time::Duration::ZERO, run_cap_usd: rust_decimal::Decimal::ONE }; +/// ``` +/// +/// ```compile_fail +/// // and the test constructor is not in the library build +/// let _ = mind_body::control::Grant::for_test; +/// ``` +/// +/// The same preamble with the real call compiles: +/// +/// ``` +/// use eureka_pipeline::Slug; +/// let _ = mind_body::control::read_effective(&Slug("eureka".into())); +/// ``` +pub fn read_effective(instance: &Slug) -> Effective { + match control_path(&control_root(), instance) { Ok(path) => derive(&FileSource::new(path), instance), Err(_) => Effective::Held { reason: HeldReason::Undecodable }, } } -fn derive(source: &impl ControlSource, instance: &Slug) -> Effective { +#[cfg(not(test))] +fn control_root() -> PathBuf { + PathBuf::from(CONTROL_ROOT) +} + +/// The test seam: this crate's own tests move the root for their thread. It +/// does not exist in a library build, and it is not a cargo feature. +#[cfg(test)] +thread_local! { + static TEST_ROOT: std::cell::RefCell> = const { std::cell::RefCell::new(None) }; +} + +#[cfg(test)] +fn control_root() -> PathBuf { + TEST_ROOT.with(|root| root.borrow().clone()).unwrap_or_else(|| PathBuf::from(CONTROL_ROOT)) +} + +/// Runs `body` with the reader pointed at `root` on this thread. +#[cfg(test)] +pub(crate) fn with_root(root: &Path, body: impl FnOnce() -> T) -> T { + let before = TEST_ROOT.with(|cell| cell.replace(Some(root.to_path_buf()))); + let result = body(); + TEST_ROOT.with(|cell| cell.replace(before)); + result +} + +/// The derivation over any source. The reader and the CLI's `show` (display +/// only: its output is text) are its callers; nothing public returns its result +/// for a source of the caller's. +pub(crate) fn derive(source: &impl ControlSource, instance: &Slug) -> Effective { match load_state(source) { Ok(state) => state.effective(instance), Err(_) => Effective::Held { reason: HeldReason::Undecodable }, } } -/// The same derivation over a fake store, for tests of the reader; the Grant it -/// makes names the instance given, and the launch judges that like any other. #[cfg(test)] -pub(crate) fn read_effective_from(source: &impl ControlSource, instance: &Slug) -> Effective { - derive(source, instance) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn a_source_that_fails_reads_as_held() { - struct Failing; - impl ControlSource for Failing { - fn snapshot(&self) -> Result> { - Err(anyhow::anyhow!("disk gone")) - } - } - assert_eq!(read_effective_from(&Failing, &Slug("eureka".into())), Effective::Held { reason: HeldReason::Undecodable }); - } -} +mod tests; /// The operator's writer over one control file. Each setter replaces its /// document whole; the other document is untouched. The file is created on the diff --git a/crates/mind-body/src/control/tests.rs b/crates/mind-body/src/control/tests.rs new file mode 100644 index 0000000..d4bb8fd --- /dev/null +++ b/crates/mind-body/src/control/tests.rs @@ -0,0 +1,414 @@ +//! The reader's tests, in the crate because they move the reader's root with +//! the `#[cfg(test)]` seam: no caller outside the crate can. Every test works in +//! a temporary directory through the same file door the units use; nothing +//! touches `/etc`. They run as root in the verify container like the writer's. + +use std::path::{Path, PathBuf}; +use std::time::Duration; + +use chrono::{DateTime, TimeZone, Utc}; +use cultcache_rs::{CultCache, DatabaseEntry, SingleFileMessagePackBackingStore}; +use eureka_pipeline::Slug; +use rust_decimal::Decimal; +use std::str::FromStr; + +use super::*; + +fn dec(text: &str) -> Decimal { + Decimal::from_str(text).unwrap() +} + +fn at(seconds: i64) -> DateTime { + Utc.timestamp_opt(1_800_000_000 + seconds, 0).unwrap() +} + +fn dial(heat: &str, cooldown: u32, run_usd: &str) -> BurnRate { + BurnRate { heat: dec(heat), base_cooldown_s: cooldown, base_run_usd: dec(run_usd), set_at: at(0), set_by: "op".into() } +} + +/// The store of the instance `eureka` under the root `dir`. +fn store_path(dir: &Path) -> PathBuf { + dir.join("eureka").join("control.cc") +} + +fn held(reason: HeldReason) -> Effective { + Effective::Held { reason } +} + +/// A written store: brake released and the given dial, through the real writer. +fn released_store(dir: &Path, dial: BurnRate) -> PathBuf { + let path = store_path(dir); + let mut writer = ControlWriter::open(&path).unwrap(); + writer.set_brake(true, at(0), "op").unwrap(); + writer.set_dial(dial).unwrap(); + path +} + +/// The real reader's answer for the store at `//control.cc`, +/// with the reader's root moved to `` on this thread. +fn effective(path: &Path) -> Effective { + let directory = path.parent().unwrap(); + let instance = Slug(directory.file_name().unwrap().to_str().unwrap().into()); + with_root(directory.parent().unwrap(), || read_effective(&instance)) +} + +/// A dial written around the writer's bounds check, so the reader's own check +/// is what is under test. +fn write_unchecked_dial(path: &Path, dial: &BurnRate) { + let mut cache = CultCache::new(); + cache.register_entry_type::().unwrap(); + cache.register_entry_type::().unwrap(); + cache.add_generic_backing_store(SingleFileMessagePackBackingStore::new(path)).unwrap(); + cache.pull_all_backing_stores().unwrap(); + cache.put(DIAL_KEY, dial).unwrap(); +} + +#[test] +fn a_source_that_fails_reads_as_held() { + struct Failing; + impl ControlSource for Failing { + fn snapshot(&self) -> Result> { + Err(anyhow::anyhow!("disk gone")) + } + } + assert_eq!(derive(&Failing, &Slug("eureka".into())), Effective::Held { reason: HeldReason::Undecodable }); +} + +#[test] +fn an_absent_store_reads_as_held() { + let dir = tempfile::tempdir().unwrap(); + assert_eq!(effective(&store_path(dir.path())), held(HeldReason::BrakeAbsent)); + assert!(!store_path(dir.path()).exists(), "reading must not create the store"); +} + +#[test] +fn an_absent_brake_reads_as_held_even_with_a_dial() { + let dir = tempfile::tempdir().unwrap(); + let path = store_path(dir.path()); + ControlWriter::open(&path).unwrap().set_dial(dial("1", 3600, "1")).unwrap(); + assert_eq!(effective(&path), held(HeldReason::BrakeAbsent)); +} + +#[test] +fn an_undecodable_store_reads_as_held() { + let dir = tempfile::tempdir().unwrap(); + let path = released_store(dir.path(), dial("1", 3600, "1")); + let good = std::fs::read(&path).unwrap(); + + // Garbage in place of the store. + std::fs::write(&path, b"this is not a cultcache store").unwrap(); + assert_eq!(effective(&path), held(HeldReason::Undecodable)); + + // A store cut off mid-document. + std::fs::write(&path, &good[..good.len() / 2]).unwrap(); + assert_eq!(effective(&path), held(HeldReason::Undecodable)); + + // The untouched store is still released, so the two reads above were the damage. + std::fs::write(&path, &good).unwrap(); + assert!(matches!(effective(&path), Effective::Released(_))); +} + +#[test] +fn a_path_that_cannot_be_read_as_a_file_reads_as_held() { + let dir = tempfile::tempdir().unwrap(); + // A directory sits where the store should be: it exists and cannot be read as a file. + let path = store_path(dir.path()); + std::fs::create_dir_all(&path).unwrap(); + assert_eq!(effective(&path), held(HeldReason::Undecodable)); +} + +#[test] +fn an_unknown_document_type_makes_the_store_undecodable() { + #[derive(Clone, Debug, PartialEq, Eq, DatabaseEntry)] + #[cultcache(type = "eureka.control.something_else.v1", schema = "Other")] + struct Other { + #[cultcache(key = 0)] + value: u32, + } + let dir = tempfile::tempdir().unwrap(); + let path = released_store(dir.path(), dial("1", 3600, "1")); + let mut cache = CultCache::new(); + cache.register_entry_type::().unwrap(); + cache.add_generic_backing_store(SingleFileMessagePackBackingStore::new(&path)).unwrap(); + cache.put("x", &Other { value: 1 }).unwrap(); + // The store now holds a released brake, a good dial and a foreign document. + assert_eq!(effective(&path), held(HeldReason::Undecodable)); +} + +#[test] +fn a_brake_payload_that_does_not_decode_makes_the_store_undecodable() { + // Same type id as the brake, a different shape: the payload cannot decode as a Brake. + #[derive(Clone, Debug, PartialEq, Eq, DatabaseEntry)] + #[cultcache(type = "eureka.control.brake.v1", schema = "ControlBrake")] + struct WrongBrake { + #[cultcache(key = 0)] + released: String, + } + let dir = tempfile::tempdir().unwrap(); + let path = store_path(dir.path()); + let mut cache = CultCache::new(); + cache.register_entry_type::().unwrap(); + cache.add_generic_backing_store(SingleFileMessagePackBackingStore::new(&path)).unwrap(); + cache.put(BRAKE_KEY, &WrongBrake { released: "yes".into() }).unwrap(); + assert_eq!(effective(&path), held(HeldReason::Undecodable)); +} + +#[test] +fn a_decode_failure_outranks_a_held_brake() { + let dir = tempfile::tempdir().unwrap(); + let path = released_store(dir.path(), dial("1", 3600, "1")); + ControlWriter::open(&path).unwrap().set_brake(false, at(5), "op").unwrap(); + assert_eq!(effective(&path), held(HeldReason::BrakeHeld)); + std::fs::write(&path, b"junk").unwrap(); + assert_eq!(effective(&path), held(HeldReason::Undecodable)); +} + +#[test] +fn a_released_brake_with_no_dial_reads_held_dial_absent() { + let dir = tempfile::tempdir().unwrap(); + let path = store_path(dir.path()); + ControlWriter::open(&path).unwrap().set_brake(true, at(0), "op").unwrap(); + assert_eq!(effective(&path), held(HeldReason::DialAbsent)); +} + +#[test] +fn dial_bounds_are_inclusive_at_both_ends() { + for (heat, expected_in) in [("0.05", true), ("2.0", true), ("0.0499", false), ("2.0001", false), ("0", false), ("-1", false), ("3", false)] { + let dir = tempfile::tempdir().unwrap(); + let path = released_store_unchecked(dir.path(), dial(heat, 3600, "1")); + let got = effective(&path); + if expected_in { + assert!(matches!(got, Effective::Released(_)), "heat {heat}: {got:?}"); + } else { + assert_eq!(got, held(HeldReason::DialOutOfBounds), "heat {heat}"); + } + } +} + +#[test] +fn a_zero_base_cooldown_or_a_non_positive_run_cost_is_out_of_bounds() { + for bad in [dial("1", 0, "1"), dial("1", 3600, "0"), dial("1", 3600, "-0.5")] { + let dir = tempfile::tempdir().unwrap(); + let path = released_store_unchecked(dir.path(), bad.clone()); + assert_eq!(effective(&path), held(HeldReason::DialOutOfBounds), "{bad:?}"); + } + let dir = tempfile::tempdir().unwrap(); + let smallest = released_store_unchecked(dir.path(), dial("1", 1, "0.000001")); + assert!(matches!(effective(&smallest), Effective::Released(_))); +} + +fn released_store_unchecked(dir: &Path, bad: BurnRate) -> PathBuf { + let path = store_path(dir); + ControlWriter::open(&path).unwrap().set_brake(true, at(0), "op").unwrap(); + write_unchecked_dial(&path, &bad); + path +} + +#[test] +fn cadence_and_run_cap_are_the_dial_divided_and_multiplied_by_heat() { + for (heat, cooldown, run_usd, cadence, cap) in [ + ("0.5", 3600u32, "5", Duration::from_secs(7200), dec("2.5")), + ("2.0", 3600, "5", Duration::from_secs(1800), dec("10.0")), + ("1", 600, "3", Duration::from_secs(600), dec("3")), + ("0.05", 90, "4", Duration::from_secs(1800), dec("0.20")), + ("1.5", 10, "2", Duration::new(6, 666_666_667), dec("3.0")), + ("2.0", 1, "0.25", Duration::from_millis(500), dec("0.500")), + ] { + let dir = tempfile::tempdir().unwrap(); + let path = released_store(dir.path(), dial(heat, cooldown, run_usd)); + let Effective::Released(grant) = effective(&path) else { panic!("heat {heat} cooldown {cooldown} run {run_usd}: not released") }; + assert_eq!(grant.cadence(), cadence, "heat {heat} cooldown {cooldown} run {run_usd}"); + assert_eq!(grant.run_cap_usd(), cap, "heat {heat} cooldown {cooldown} run {run_usd}"); + } +} + +#[test] +fn the_operators_last_write_is_what_is_in_force_and_provenance_round_trips() { + let dir = tempfile::tempdir().unwrap(); + let path = released_store(dir.path(), dial("1", 3600, "1")); + assert!(matches!(effective(&path), Effective::Released(_))); + + ControlWriter::open(&path).unwrap().set_brake(false, at(60), "alice").unwrap(); + assert_eq!(effective(&path), held(HeldReason::BrakeHeld)); + let state = load_state(&FileSource::new(&path)).unwrap(); + let brake = state.brake.unwrap(); + assert_eq!((brake.released, brake.set_at, brake.set_by.as_str()), (false, at(60), "alice")); + // The dial survives the brake write untouched. + assert_eq!(state.dial.unwrap().set_by, "op"); + + ControlWriter::open(&path).unwrap().set_brake(true, at(120), "bob").unwrap(); + assert!(matches!(effective(&path), Effective::Released(_))); + let brake = load_state(&FileSource::new(&path)).unwrap().brake.unwrap(); + assert_eq!((brake.released, brake.set_at, brake.set_by.as_str()), (true, at(120), "bob")); +} + +#[test] +fn reading_writes_nothing_and_takes_no_lock_file() { + let dir = tempfile::tempdir().unwrap(); + let path = released_store(dir.path(), dial("1", 3600, "1")); + let store_dir = path.parent().unwrap(); + let listing = |dir: &Path| { + let mut names: Vec<_> = std::fs::read_dir(dir).unwrap().map(|e| e.unwrap().file_name()).collect(); + names.sort(); + names + }; + // The writer left its sibling lock file; remove it, so a reader that wants one has to create it. + for entry in std::fs::read_dir(store_dir).unwrap() { + let entry = entry.unwrap().path(); + if entry != path { + std::fs::remove_file(entry).unwrap(); + } + } + let before = (listing(store_dir), std::fs::read(&path).unwrap()); + assert!(matches!(effective(&path), Effective::Released(_))); + assert_eq!((listing(store_dir), std::fs::read(&path).unwrap()), before); +} + + +/// A dial document under the dial's type id whose payload is not a dial. +#[derive(Clone, Debug, PartialEq, Eq, DatabaseEntry)] +#[cultcache(type = "eureka.control.burn_rate.v1", schema = "ControlBurnRate")] +struct WrongDial { + #[cultcache(key = 0)] + heat: String, +} + +fn write_wrong_dial(path: &Path, heat: &str) { + let mut cache = CultCache::new(); + cache.register_entry_type::().unwrap(); + cache.register_entry_type::().unwrap(); + cache.add_generic_backing_store(SingleFileMessagePackBackingStore::new(path)).unwrap(); + cache.pull_all_backing_stores().unwrap(); + cache.put(mind_body::control::DIAL_KEY, &WrongDial { heat: heat.into() }).unwrap(); +} + +#[test] +fn a_dial_that_does_not_decode_reads_held_whatever_the_brake_says() { + for brake_released in [true, false] { + for heat in ["not a number", "NaN", ""] { + let dir = tempfile::tempdir().unwrap(); + let path = store_path(dir.path()); + ControlWriter::open(&path).unwrap().set_brake(brake_released, at(0), "op").unwrap(); + write_wrong_dial(&path, heat); + assert_eq!( + effective(&path), + held(HeldReason::Undecodable), + "brake released={brake_released}, dial heat {heat:?}: a dial that cannot be read is never replaced by a default" + ); + } + } +} + + +mod grant { + //! A Grant is the proof that this instance's own control store released: the + //! reader derives the path from the instance, and a launch refuses a Grant + //! read for another instance before it asks the mind anything. + + use anyhow::{Result, anyhow}; + use cultnet_rs::Selection; + use eureka_pipeline::{PipelineDocument, RunTurn}; + use huginn_mind::{PipelineAdmissionOutcome, PipelineSelectionPage}; + + use super::*; + use crate::launch::{Clock, Declined, LaunchOutcome, LaunchRequest, Launcher, Ports, open_and_launch}; + use crate::queue::MindPort; + + fn slug(name: &str) -> Slug { + Slug(name.into()) + } + + fn release(root: &Path, instance: &str) { + let mut writer = ControlWriter::open(control_path(root, &slug(instance)).unwrap()).unwrap(); + writer.set_brake(true, at(0), "op").unwrap(); + writer.set_dial(dial("1", 3600, "5")).unwrap(); + } + + fn read(root: &Path, instance: &str) -> Effective { + with_root(root, || read_effective(&slug(instance))) + } + + /// A mind that answers nothing: a launch that reaches it has gone past the + /// instance check. + struct SilentMind(Slug); + + impl MindPort for SilentMind { + fn instance(&self) -> &Slug { + &self.0 + } + + fn query(&self, _selection: &Selection) -> Result { + Err(anyhow!("the launch asked the mind")) + } + + fn admit(&self, _agent: &str, _session: &str, _documents: Vec) -> Result { + Err(anyhow!("the launch admitted")) + } + } + + struct NoUnits; + + impl Launcher for NoUnits { + fn start(&self, _instance: &Slug, _turn: RunTurn) -> Result<()> { + Err(anyhow!("the launch started a unit")) + } + + fn alive(&self, _instance: &Slug, _turn: RunTurn) -> bool { + false + } + } + + struct Now; + + impl Clock for Now { + fn now(&self) -> DateTime { + at(100) + } + } + + fn launch_as(mind_instance: &str, grant: Grant, turn: RunTurn) -> Result { + let mind = SilentMind(slug(mind_instance)); + let ports = Ports { mind: &mind, launcher: &NoUnits, clock: &Now, host: "host" }; + open_and_launch(&ports, LaunchRequest { turn, claims: vec![], agent: "agent".into(), grant }) + } + + #[test] + fn the_reader_makes_a_grant_for_the_instance_whose_store_it_read() { + let root = tempfile::tempdir().unwrap(); + release(root.path(), "void"); + let Effective::Released(grant) = read(root.path(), "void") else { panic!("void's store is released") }; + assert_eq!(grant.instance(), &slug("void")); + assert_eq!(grant.run_cap_usd(), dec("5")); + } + + #[test] + fn another_instances_released_store_yields_no_grant_for_this_one() { + let root = tempfile::tempdir().unwrap(); + release(root.path(), "void"); + assert_eq!(read(root.path(), "eureka"), held(HeldReason::BrakeAbsent)); + for walk in ["../void", "void/..", "eureka/../void", "/void"] { + assert_eq!(read(root.path(), walk), held(HeldReason::Undecodable), "{walk}"); + } + } + + #[test] + fn a_grant_read_for_one_instance_cannot_launch_another() { + let root = tempfile::tempdir().unwrap(); + release(root.path(), "void"); + for turn in [RunTurn::SelfRun, RunTurn::PersonaTurn] { + let Effective::Released(grant) = read(root.path(), "void") else { panic!("void's store is released") }; + // The mind is eureka's; the silent mind and the unit-less launcher fail loudly if the launch goes on. + let outcome = launch_as("eureka", grant, turn).unwrap(); + assert_eq!(outcome, LaunchOutcome::Refused(Declined::GrantForOtherInstance), "{turn:?}"); + } + } + + #[test] + fn the_matching_grant_is_not_stopped_by_the_instance_check() { + let root = tempfile::tempdir().unwrap(); + release(root.path(), "void"); + let Effective::Released(grant) = read(root.path(), "void") else { panic!("void's store is released") }; + let error = launch_as("void", grant, RunTurn::SelfRun).unwrap_err(); + assert!(format!("{error:#}").contains("asked the mind"), "{error:#}"); + } +} diff --git a/crates/mind-body/src/launch.rs b/crates/mind-body/src/launch.rs index c3ed530..4cb3190 100644 --- a/crates/mind-body/src/launch.rs +++ b/crates/mind-body/src/launch.rs @@ -850,19 +850,25 @@ mod tests { fn a_grant_read_for_another_instance_opens_nothing_and_starts_nothing() { let rig = Rig::new(); rig.mind.committed(vec![spec("x")]); - for turn in [RunTurn::SelfRun, RunTurn::PersonaTurn] { - let claims = if turn == RunTurn::SelfRun { vec![spec_ref("x")] } else { vec![] }; - let ports = Ports { mind: &rig.mind, launcher: &rig.launcher, clock: &rig.clock, host: "yggdrasil-host" }; - let grant = Grant::for_test(Slug("another-instance".into()), StdDuration::from_secs(60), five()); - let outcome = open_and_launch(&ports, LaunchRequest { turn, claims, agent: "agent-x".into(), grant }).unwrap(); - assert_eq!(outcome, LaunchOutcome::Refused(Declined::GrantForOtherInstance), "{turn:?}"); + // Forgeries shaped like the real name: the same length and prefix, differing only at the end; + // a prefix of it; and a name it is a prefix of. Each is checked whole. + let forged = [format!("{}k", &INSTANCE[..INSTANCE.len() - 1]), INSTANCE[..INSTANCE.len() - 1].to_string(), format!("{INSTANCE}-test"), "another-instance".to_string()]; + assert_eq!(forged[0].len(), INSTANCE.len()); + for name in forged { + for turn in [RunTurn::SelfRun, RunTurn::PersonaTurn] { + let claims = if turn == RunTurn::SelfRun { vec![spec_ref("x")] } else { vec![] }; + let ports = Ports { mind: &rig.mind, launcher: &rig.launcher, clock: &rig.clock, host: "yggdrasil-host" }; + let grant = Grant::for_test(Slug(name.clone()), StdDuration::from_secs(60), five()); + let outcome = open_and_launch(&ports, LaunchRequest { turn, claims, agent: "agent-x".into(), grant }).unwrap(); + assert_eq!(outcome, LaunchOutcome::Refused(Declined::GrantForOtherInstance), "{name} {turn:?}"); + } } assert_eq!((rig.runs(), rig.started()), (0, 0)); assert!(matches!(rig.launch(RunTurn::SelfRun, &[spec_ref("x")], five()), LaunchOutcome::Launched { .. }), "the mind's own Grant launches"); } #[test] - fn a_persona_turn_with_claims_is_refused_and_opens_nothing() { + fn a_persona_turn_with_a_claim_of_any_kind_is_refused_and_opens_nothing() { let rig = Rig::new(); rig.mind.committed(vec![spec("x"), spec("blocked")]); rig.mind.committed(vec![question_in("fork", &spec_ref("blocked"))]); @@ -871,6 +877,11 @@ mod tests { let outcome = rig.launch(RunTurn::PersonaTurn, &[spec_ref(item)], five()); assert_eq!(outcome, LaunchOutcome::Refused(Declined::PersonaRunTakesNoClaims), "{item}"); } + // And a claim of every kind the leaf has, whether or not the document exists. + for kind in PipelineKind::ALL { + let outcome = rig.launch(RunTurn::PersonaTurn, &[reference(*kind, "no-such-document")], five()); + assert_eq!(outcome, LaunchOutcome::Refused(Declined::PersonaRunTakesNoClaims), "{kind:?}"); + } assert_eq!((rig.runs(), rig.started()), (0, 0)); assert!(matches!(rig.launch(RunTurn::PersonaTurn, &[], five()), LaunchOutcome::Launched { .. })); } diff --git a/crates/mind-body/tests/control.rs b/crates/mind-body/tests/control.rs index 8f7f191..84f13e0 100644 --- a/crates/mind-body/tests/control.rs +++ b/crates/mind-body/tests/control.rs @@ -14,9 +14,7 @@ use std::time::Duration; use chrono::{DateTime, TimeZone, Utc}; use cultcache_rs::{CultCache, DatabaseEntry, SingleFileMessagePackBackingStore}; -use mind_body::control::{ - BRAKE_KEY, BurnRate, ControlWriter, Effective, FileSource, HeldReason, control_path, load_state, read_effective, -}; +use mind_body::control::{BurnRate, ControlWriter, FileSource, control_path, load_state}; use eureka_pipeline::Slug; use rust_decimal::Decimal; use std::str::FromStr; @@ -38,10 +36,6 @@ fn store_path(dir: &Path) -> PathBuf { dir.join("eureka").join("control.cc") } -fn held(reason: HeldReason) -> Effective { - Effective::Held { reason } -} - /// A written store: brake released and the given dial, through the real writer. fn released_store(dir: &Path, dial: BurnRate) -> PathBuf { let path = store_path(dir); @@ -51,193 +45,6 @@ fn released_store(dir: &Path, dial: BurnRate) -> PathBuf { path } -/// The reader's answer for the store at `//control.cc`: the -/// reader derives that path itself from the root and the instance. -fn effective(path: &Path) -> Effective { - let directory = path.parent().unwrap(); - let instance = Slug(directory.file_name().unwrap().to_str().unwrap().into()); - read_effective(directory.parent().unwrap(), &instance) -} - -/// A dial written around the writer's bounds check, so the reader's own check -/// is what is under test. -fn write_unchecked_dial(path: &Path, dial: &BurnRate) { - let mut cache = CultCache::new(); - cache.register_entry_type::().unwrap(); - cache.register_entry_type::().unwrap(); - cache.add_generic_backing_store(SingleFileMessagePackBackingStore::new(path)).unwrap(); - cache.pull_all_backing_stores().unwrap(); - cache.put(mind_body::control::DIAL_KEY, dial).unwrap(); -} - -#[test] -fn an_absent_store_reads_as_held() { - let dir = tempfile::tempdir().unwrap(); - assert_eq!(effective(&store_path(dir.path())), held(HeldReason::BrakeAbsent)); - assert!(!store_path(dir.path()).exists(), "reading must not create the store"); -} - -#[test] -fn an_absent_brake_reads_as_held_even_with_a_dial() { - let dir = tempfile::tempdir().unwrap(); - let path = store_path(dir.path()); - ControlWriter::open(&path).unwrap().set_dial(dial("1", 3600, "1")).unwrap(); - assert_eq!(effective(&path), held(HeldReason::BrakeAbsent)); -} - -#[test] -fn an_undecodable_store_reads_as_held() { - let dir = tempfile::tempdir().unwrap(); - let path = released_store(dir.path(), dial("1", 3600, "1")); - let good = std::fs::read(&path).unwrap(); - - // Garbage in place of the store. - std::fs::write(&path, b"this is not a cultcache store").unwrap(); - assert_eq!(effective(&path), held(HeldReason::Undecodable)); - - // A store cut off mid-document. - std::fs::write(&path, &good[..good.len() / 2]).unwrap(); - assert_eq!(effective(&path), held(HeldReason::Undecodable)); - - // The untouched store is still released, so the two reads above were the damage. - std::fs::write(&path, &good).unwrap(); - assert!(matches!(effective(&path), Effective::Released(_))); -} - -#[test] -fn a_path_that_cannot_be_read_as_a_file_reads_as_held() { - let dir = tempfile::tempdir().unwrap(); - // A directory sits where the store should be: it exists and cannot be read as a file. - let path = store_path(dir.path()); - std::fs::create_dir_all(&path).unwrap(); - assert_eq!(effective(&path), held(HeldReason::Undecodable)); -} - -#[test] -fn an_unknown_document_type_makes_the_store_undecodable() { - #[derive(Clone, Debug, PartialEq, Eq, DatabaseEntry)] - #[cultcache(type = "eureka.control.something_else.v1", schema = "Other")] - struct Other { - #[cultcache(key = 0)] - value: u32, - } - let dir = tempfile::tempdir().unwrap(); - let path = released_store(dir.path(), dial("1", 3600, "1")); - let mut cache = CultCache::new(); - cache.register_entry_type::().unwrap(); - cache.add_generic_backing_store(SingleFileMessagePackBackingStore::new(&path)).unwrap(); - cache.put("x", &Other { value: 1 }).unwrap(); - // The store now holds a released brake, a good dial and a foreign document. - assert_eq!(effective(&path), held(HeldReason::Undecodable)); -} - -#[test] -fn a_brake_payload_that_does_not_decode_makes_the_store_undecodable() { - // Same type id as the brake, a different shape: the payload cannot decode as a Brake. - #[derive(Clone, Debug, PartialEq, Eq, DatabaseEntry)] - #[cultcache(type = "eureka.control.brake.v1", schema = "ControlBrake")] - struct WrongBrake { - #[cultcache(key = 0)] - released: String, - } - let dir = tempfile::tempdir().unwrap(); - let path = store_path(dir.path()); - let mut cache = CultCache::new(); - cache.register_entry_type::().unwrap(); - cache.add_generic_backing_store(SingleFileMessagePackBackingStore::new(&path)).unwrap(); - cache.put(BRAKE_KEY, &WrongBrake { released: "yes".into() }).unwrap(); - assert_eq!(effective(&path), held(HeldReason::Undecodable)); -} - -#[test] -fn a_decode_failure_outranks_a_held_brake() { - let dir = tempfile::tempdir().unwrap(); - let path = released_store(dir.path(), dial("1", 3600, "1")); - ControlWriter::open(&path).unwrap().set_brake(false, at(5), "op").unwrap(); - assert_eq!(effective(&path), held(HeldReason::BrakeHeld)); - std::fs::write(&path, b"junk").unwrap(); - assert_eq!(effective(&path), held(HeldReason::Undecodable)); -} - -#[test] -fn a_released_brake_with_no_dial_reads_held_dial_absent() { - let dir = tempfile::tempdir().unwrap(); - let path = store_path(dir.path()); - ControlWriter::open(&path).unwrap().set_brake(true, at(0), "op").unwrap(); - assert_eq!(effective(&path), held(HeldReason::DialAbsent)); -} - -#[test] -fn dial_bounds_are_inclusive_at_both_ends() { - for (heat, expected_in) in [("0.05", true), ("2.0", true), ("0.0499", false), ("2.0001", false), ("0", false), ("-1", false), ("3", false)] { - let dir = tempfile::tempdir().unwrap(); - let path = released_store_unchecked(dir.path(), dial(heat, 3600, "1")); - let got = effective(&path); - if expected_in { - assert!(matches!(got, Effective::Released(_)), "heat {heat}: {got:?}"); - } else { - assert_eq!(got, held(HeldReason::DialOutOfBounds), "heat {heat}"); - } - } -} - -#[test] -fn a_zero_base_cooldown_or_a_non_positive_run_cost_is_out_of_bounds() { - for bad in [dial("1", 0, "1"), dial("1", 3600, "0"), dial("1", 3600, "-0.5")] { - let dir = tempfile::tempdir().unwrap(); - let path = released_store_unchecked(dir.path(), bad.clone()); - assert_eq!(effective(&path), held(HeldReason::DialOutOfBounds), "{bad:?}"); - } - let dir = tempfile::tempdir().unwrap(); - let smallest = released_store_unchecked(dir.path(), dial("1", 1, "0.000001")); - assert!(matches!(effective(&smallest), Effective::Released(_))); -} - -fn released_store_unchecked(dir: &Path, bad: BurnRate) -> PathBuf { - let path = store_path(dir); - ControlWriter::open(&path).unwrap().set_brake(true, at(0), "op").unwrap(); - write_unchecked_dial(&path, &bad); - path -} - -#[test] -fn cadence_and_run_cap_are_the_dial_divided_and_multiplied_by_heat() { - for (heat, cooldown, run_usd, cadence, cap) in [ - ("0.5", 3600u32, "5", Duration::from_secs(7200), dec("2.5")), - ("2.0", 3600, "5", Duration::from_secs(1800), dec("10.0")), - ("1", 600, "3", Duration::from_secs(600), dec("3")), - ("0.05", 90, "4", Duration::from_secs(1800), dec("0.20")), - ("1.5", 10, "2", Duration::new(6, 666_666_667), dec("3.0")), - ("2.0", 1, "0.25", Duration::from_millis(500), dec("0.500")), - ] { - let dir = tempfile::tempdir().unwrap(); - let path = released_store(dir.path(), dial(heat, cooldown, run_usd)); - let Effective::Released(grant) = effective(&path) else { panic!("heat {heat} cooldown {cooldown} run {run_usd}: not released") }; - assert_eq!(grant.cadence(), cadence, "heat {heat} cooldown {cooldown} run {run_usd}"); - assert_eq!(grant.run_cap_usd(), cap, "heat {heat} cooldown {cooldown} run {run_usd}"); - } -} - -#[test] -fn the_operators_last_write_is_what_is_in_force_and_provenance_round_trips() { - let dir = tempfile::tempdir().unwrap(); - let path = released_store(dir.path(), dial("1", 3600, "1")); - assert!(matches!(effective(&path), Effective::Released(_))); - - ControlWriter::open(&path).unwrap().set_brake(false, at(60), "alice").unwrap(); - assert_eq!(effective(&path), held(HeldReason::BrakeHeld)); - let state = load_state(&FileSource::new(&path)).unwrap(); - let brake = state.brake.unwrap(); - assert_eq!((brake.released, brake.set_at, brake.set_by.as_str()), (false, at(60), "alice")); - // The dial survives the brake write untouched. - assert_eq!(state.dial.unwrap().set_by, "op"); - - ControlWriter::open(&path).unwrap().set_brake(true, at(120), "bob").unwrap(); - assert!(matches!(effective(&path), Effective::Released(_))); - let brake = load_state(&FileSource::new(&path)).unwrap().brake.unwrap(); - assert_eq!((brake.released, brake.set_at, brake.set_by.as_str()), (true, at(120), "bob")); -} - #[test] fn setting_the_dial_replaces_it_whole_and_keeps_the_brake() { let dir = tempfile::tempdir().unwrap(); @@ -262,28 +69,6 @@ fn the_writer_refuses_an_out_of_bounds_dial_and_leaves_the_store_unchanged() { } } -#[test] -fn reading_writes_nothing_and_takes_no_lock_file() { - let dir = tempfile::tempdir().unwrap(); - let path = released_store(dir.path(), dial("1", 3600, "1")); - let store_dir = path.parent().unwrap(); - let listing = |dir: &Path| { - let mut names: Vec<_> = std::fs::read_dir(dir).unwrap().map(|e| e.unwrap().file_name()).collect(); - names.sort(); - names - }; - // The writer left its sibling lock file; remove it, so a reader that wants one has to create it. - for entry in std::fs::read_dir(store_dir).unwrap() { - let entry = entry.unwrap().path(); - if entry != path { - std::fs::remove_file(entry).unwrap(); - } - } - let before = (listing(store_dir), std::fs::read(&path).unwrap()); - assert!(matches!(effective(&path), Effective::Released(_))); - assert_eq!((listing(store_dir), std::fs::read(&path).unwrap()), before); -} - #[test] fn the_control_path_is_derived_from_a_valid_instance_only() { let root = Path::new(mind_body::control::CONTROL_ROOT); @@ -292,41 +77,6 @@ fn the_control_path_is_derived_from_a_valid_instance_only() { assert!(control_path(root, &Slug(bad.into())).is_err(), "{bad:?}"); } } - -/// A dial document under the dial's type id whose payload is not a dial. -#[derive(Clone, Debug, PartialEq, Eq, DatabaseEntry)] -#[cultcache(type = "eureka.control.burn_rate.v1", schema = "ControlBurnRate")] -struct WrongDial { - #[cultcache(key = 0)] - heat: String, -} - -fn write_wrong_dial(path: &Path, heat: &str) { - let mut cache = CultCache::new(); - cache.register_entry_type::().unwrap(); - cache.register_entry_type::().unwrap(); - cache.add_generic_backing_store(SingleFileMessagePackBackingStore::new(path)).unwrap(); - cache.pull_all_backing_stores().unwrap(); - cache.put(mind_body::control::DIAL_KEY, &WrongDial { heat: heat.into() }).unwrap(); -} - -#[test] -fn a_dial_that_does_not_decode_reads_held_whatever_the_brake_says() { - for brake_released in [true, false] { - for heat in ["not a number", "NaN", ""] { - let dir = tempfile::tempdir().unwrap(); - let path = store_path(dir.path()); - ControlWriter::open(&path).unwrap().set_brake(brake_released, at(0), "op").unwrap(); - write_wrong_dial(&path, heat); - assert_eq!( - effective(&path), - held(HeldReason::Undecodable), - "brake released={brake_released}, dial heat {heat:?}: a dial that cannot be read is never replaced by a default" - ); - } - } -} - #[cfg(unix)] mod modes { use super::*; @@ -391,7 +141,7 @@ mod modes { ); assert!(std::fs::OpenOptions::new().write(true).open(&path).is_err()); assert!(std::fs::OpenOptions::new().write(true).open(path.with_file_name("control.cc.lock")).is_err()); - assert!(matches!(effective(&path), Effective::Released(_)), "a non-root user can still read the store"); + assert!(load_state(&FileSource::new(&path)).unwrap().brake.unwrap().released, "a non-root user can still read the store"); } other => panic!("unknown role {other}"), } diff --git a/crates/mind-body/tests/forge.rs b/crates/mind-body/tests/forge.rs new file mode 100644 index 0000000..ba7f92b --- /dev/null +++ b/crates/mind-body/tests/forge.rs @@ -0,0 +1,66 @@ +//! The brake-grant rule seen from outside the crate: the only reader takes the +//! instance, so whatever a caller builds elsewhere (a store in a temporary +//! directory, a symlink to another instance's directory, a copy of another +//! instance's store, the relative root `.`) is never what a Grant is made from. +//! The signature half of the rule (no root argument, no source reader, no +//! struct literal, no test constructor) is the `compile_fail` examples on +//! `read_effective`; this is the behavioural half, committed because it is the +//! only defence of the rule against a forged store. + +#![cfg(unix)] + +use std::path::Path; + +use chrono::{TimeZone, Utc}; +use eureka_pipeline::Slug; +use mind_body::control::{BurnRate, ControlWriter, Effective, HeldReason, control_path, read_effective}; +use rust_decimal::Decimal; + +/// A name no real install has a store for under the real root. +const FORGED: &str = "forge-probe-instance"; +const OTHER: &str = "forge-probe-other"; + +fn release(root: &Path, instance: &str) { + let mut writer = ControlWriter::open(control_path(root, &Slug(instance.into())).unwrap()).unwrap(); + let at = Utc.timestamp_opt(1_800_000_000, 0).unwrap(); + writer.set_brake(true, at, "forger").unwrap(); + writer + .set_dial(BurnRate { heat: Decimal::ONE, base_cooldown_s: 3600, base_run_usd: Decimal::new(5, 0), set_at: at, set_by: "forger".into() }) + .unwrap(); +} + +fn reads(instance: &str) -> Effective { + read_effective(&Slug(instance.into())) +} + +#[test] +fn no_forged_store_yields_a_grant() { + assert_eq!(reads(FORGED), Effective::Held { reason: HeldReason::BrakeAbsent }, "the real root has no store for the probe's name"); + + // A released store for the name in a root of the forger's choosing. + let tmp = tempfile::tempdir().unwrap(); + release(tmp.path(), FORGED); + assert!(matches!(reads(FORGED), Effective::Held { .. }), "store in a temporary root"); + + // A symlink from the name to another instance's released directory. + let linked = tempfile::tempdir().unwrap(); + release(linked.path(), OTHER); + std::os::unix::fs::symlink(linked.path().join(OTHER), linked.path().join(FORGED)).unwrap(); + assert!(matches!(reads(FORGED), Effective::Held { .. }), "symlink to another instance"); + + // A copy of another instance's store under the name. + let copied = tempfile::tempdir().unwrap(); + release(copied.path(), OTHER); + std::fs::create_dir_all(copied.path().join(FORGED)).unwrap(); + std::fs::copy(copied.path().join(OTHER).join("control.cc"), copied.path().join(FORGED).join("control.cc")).unwrap(); + assert!(matches!(reads(FORGED), Effective::Held { .. }), "copy of another instance's store"); + + // The relative root '.': the working directory holds /control.cc. + let here = tempfile::tempdir().unwrap(); + release(here.path(), FORGED); + let before = std::env::current_dir().unwrap(); + std::env::set_current_dir(here.path()).unwrap(); + let relative = reads(FORGED); + std::env::set_current_dir(before).unwrap(); + assert!(matches!(relative, Effective::Held { .. }), "relative root"); +} diff --git a/crates/mind-body/tests/grant.rs b/crates/mind-body/tests/grant.rs deleted file mode 100644 index dc4b5ec..0000000 --- a/crates/mind-body/tests/grant.rs +++ /dev/null @@ -1,114 +0,0 @@ -//! A Grant is the proof that this instance's own control store released. The -//! tests are an outside crate's view: the only reader takes a root and an -//! instance and derives the store path itself, and a launch refuses a Grant -//! read for another instance before it asks the mind anything. - -use std::path::Path; - -use anyhow::{Result, anyhow}; -use chrono::{DateTime, TimeZone, Utc}; -use cultnet_rs::Selection; -use eureka_pipeline::{PipelineDocument, RunTurn, Slug}; -use huginn_mind::{PipelineAdmissionOutcome, PipelineSelectionPage}; -use mind_body::control::{BurnRate, ControlWriter, Effective, HeldReason, control_path, read_effective}; -use mind_body::launch::{Clock, Declined, LaunchOutcome, LaunchRequest, Launcher, Ports, open_and_launch}; -use mind_body::queue::MindPort; -use rust_decimal::Decimal; - -fn slug(name: &str) -> Slug { - Slug(name.into()) -} - -fn release(root: &Path, instance: &Slug) { - let mut writer = ControlWriter::open(control_path(root, instance).unwrap()).unwrap(); - let at = Utc.timestamp_opt(1_800_000_000, 0).unwrap(); - writer.set_brake(true, at, "op").unwrap(); - writer.set_dial(BurnRate { heat: Decimal::ONE, base_cooldown_s: 3600, base_run_usd: Decimal::new(5, 0), set_at: at, set_by: "op".into() }).unwrap(); -} - -/// A mind that answers nothing: a launch that reaches it has gone past the -/// instance check. -struct SilentMind(Slug); - -impl MindPort for SilentMind { - fn instance(&self) -> &Slug { - &self.0 - } - - fn query(&self, _selection: &Selection) -> Result { - Err(anyhow!("the launch asked the mind")) - } - - fn admit(&self, _agent: &str, _session: &str, _documents: Vec) -> Result { - Err(anyhow!("the launch admitted")) - } -} - -struct NoUnits; - -impl Launcher for NoUnits { - fn start(&self, _instance: &Slug, _turn: RunTurn) -> Result<()> { - Err(anyhow!("the launch started a unit")) - } - - fn alive(&self, _instance: &Slug, _turn: RunTurn) -> bool { - false - } -} - -struct Now; - -impl Clock for Now { - fn now(&self) -> DateTime { - Utc.timestamp_opt(1_800_000_100, 0).unwrap() - } -} - -fn launch_as(mind_instance: &str, grant: mind_body::control::Grant, turn: RunTurn) -> Result { - let mind = SilentMind(slug(mind_instance)); - let ports = Ports { mind: &mind, launcher: &NoUnits, clock: &Now, host: "host" }; - open_and_launch(&ports, LaunchRequest { turn, claims: vec![], agent: "agent".into(), grant }) -} - -#[test] -fn the_reader_makes_a_grant_for_the_instance_whose_store_it_read() { - let root = tempfile::tempdir().unwrap(); - release(root.path(), &slug("void")); - let Effective::Released(grant) = read_effective(root.path(), &slug("void")) else { panic!("void's store is released") }; - assert_eq!(grant.instance(), &slug("void")); - assert_eq!(grant.run_cap_usd(), Decimal::new(5, 0)); -} - -#[test] -fn another_instances_released_store_yields_no_grant_for_this_one() { - let root = tempfile::tempdir().unwrap(); - release(root.path(), &slug("void")); - // The operator holds eureka's brake: eureka has no released store under this root. - assert_eq!(read_effective(root.path(), &slug("eureka")), Effective::Held { reason: HeldReason::BrakeAbsent }); - // A path-shaped instance cannot reach void's store by walking. - for walk in ["../void", "void/..", "eureka/../void", "/void"] { - assert_eq!(read_effective(root.path(), &slug(walk)), Effective::Held { reason: HeldReason::Undecodable }, "{walk}"); - } -} - -#[test] -fn a_grant_read_for_one_instance_cannot_launch_another() { - let root = tempfile::tempdir().unwrap(); - release(root.path(), &slug("void")); - for turn in [RunTurn::SelfRun, RunTurn::PersonaTurn] { - let Effective::Released(grant) = read_effective(root.path(), &slug("void")) else { panic!("void's store is released") }; - // The mind is eureka's; the silent mind and the unit-less launcher fail loudly if the launch goes on. - let outcome = launch_as("eureka", grant, turn).unwrap(); - assert_eq!(outcome, LaunchOutcome::Refused(Declined::GrantForOtherInstance), "{turn:?}"); - } -} - -#[test] -fn the_matching_grant_is_not_stopped_by_the_instance_check() { - let root = tempfile::tempdir().unwrap(); - release(root.path(), &slug("void")); - let Effective::Released(grant) = read_effective(root.path(), &slug("void")) else { panic!("void's store is released") }; - // Past the check the silent mind is asked, so the launch errors there. - let error = launch_as("void", grant, RunTurn::SelfRun).unwrap_err(); - assert!(format!("{error:#}").contains("asked the mind"), "{error:#}"); -}