mod world;
use world::{AGENT, World};
use super::*;
use crate::boundary::dispatch::Deps;
use crate::cli_outbound::Cli;
use crate::control::classify::Effect;
use crate::control::judge::{Answer, Answers, Scope, Standing};
use crate::control::policy::CAPABILITY_YAML;
use crate::control::wire::Request;
use crate::opslog::{DETACHED_EXIT, tail};
use std::path::{Path, PathBuf};
#[test]
fn an_answer_writes_the_row_the_control_folds_and_launches_the_release() {
let world = World::new();
world.repo();
world.park(AGENT, "toolu_42");
let reply = answer_hold(
&world.deps(),
"1000",
&world.workspace(),
AGENT,
Answer::once(Ruling::Pass),
)
.expect("something is parked");
assert_eq!(
reply,
Reply::Answered(Answered {
tool_use: "toolu_42".to_owned(),
tool: "bash".to_owned(),
answer: Answer::once(Ruling::Pass),
advanced: true,
})
);
let rows = tail(&world.state(), usize::MAX);
let answer = rows.first().expect("the answer row");
assert_eq!(
answer.argv,
vec!["yog-control", "answer", "toolu_42", "pass", "call"]
);
assert_eq!(
Answers::fold(&rows).ruling(
&request("toolu_42", "bash", AGENT),
&crate::nav::ws_key(&world.workspace()),
Effect::Destructive,
&crate::control::policy::Policy::default(),
),
Standing {
ruling: Ruling::Pass,
scope: Scope::Call
},
);
let advance = rows.get(1).expect("the advance row");
assert_eq!(advance.argv.get(1).map(String::as_str), Some("advance"));
assert_eq!(advance.exit, DETACHED_EXIT);
}
#[test]
fn the_answer_is_reachable_from_the_chokepoint_every_seat_enters() {
let world = World::new();
world.repo();
world.park(AGENT, "toolu_1");
let mut ui = crate::ui_state::UiState::open(PathBuf::from("/nonexistent/ui.json"));
let through = crate::boundary::dispatch::dispatch(
&world.deps(),
&mut ui,
"1000",
&crate::boundary::Action::AnswerHold {
workspace: crate::naming::leaf(&(world.workspace())),
agent: AGENT.to_owned(),
answer: Answer::once(Ruling::Hold),
},
);
assert!(matches!(through, Ok(Reply::Answered(_))));
}
#[test]
fn keeping_it_parked_writes_the_row_and_launches_nothing() {
let world = World::new();
world.repo();
world.park(AGENT, "toolu_7");
let reply = answer_hold(
&world.deps(),
"1000",
&world.workspace(),
AGENT,
Answer::once(Ruling::Hold),
)
.expect("something is parked");
assert!(matches!(
reply,
Reply::Answered(Answered {
advanced: false,
answer: Answer {
ruling: Ruling::Hold,
..
},
..
})
));
let rows = tail(&world.state(), usize::MAX);
assert_eq!(rows.len(), 1, "a hold answer drives nothing");
}
#[test]
fn a_refusal_releases_too_because_a_decline_is_in_band() {
let world = World::new();
world.repo();
world.park(AGENT, "toolu_8");
let reply = answer_hold(
&world.deps(),
"1000",
&world.workspace(),
AGENT,
Answer::once(Ruling::Refuse),
)
.expect("something is parked");
assert!(matches!(
reply,
Reply::Answered(Answered { advanced: true, .. })
));
}
#[test]
fn a_failed_launch_is_still_an_answer_and_still_a_row() {
let world = World::new();
world.repo();
world.park(AGENT, "toolu_9");
let mut deps = world.deps();
deps.litany = Cli::new("/no/such/litany");
let reply = answer_hold(
&deps,
"1000",
&world.workspace(),
AGENT,
Answer::once(Ruling::Pass),
)
.expect("the answer is durable whatever the launch does");
assert!(matches!(
reply,
Reply::Answered(Answered {
advanced: false,
..
})
));
let rows = tail(&world.state(), usize::MAX);
assert_eq!(rows.len(), 2);
assert!(!rows[1].stderr.is_empty());
}
#[test]
fn answering_where_nothing_is_parked_refuses_and_writes_nothing() {
let world = World::new();
world.repo();
let err = answer_hold(
&world.deps(),
"1000",
&world.workspace(),
AGENT,
Answer::once(Ruling::Pass),
)
.expect_err("an answer aimed at nothing says so");
assert!(err.contains("nothing is held"), "{err}");
assert!(tail(&world.state(), usize::MAX).is_empty());
}
fn request(id: &str, name: &str, agent: &str) -> Request {
Request {
id: id.to_owned(),
name: name.to_owned(),
input: serde_json::json!({}),
role: "worker".to_owned(),
agent_id: agent.to_owned(),
}
}
mod scope;
mod confinement;
mod floor;
mod nudge;