use super::super::ctx;
use super::rt;
use crate::boundary::line::{Context, parse};
use crate::boundary::{Action, Gesture, Query};
use crate::monitor::Verb;
#[test]
fn every_hold_answer_round_trips_and_a_bad_verdict_refuses() {
use crate::control::judge::{Answer, Ruling, Scope};
for ruling in [Ruling::Pass, Ruling::Hold, Ruling::Refuse] {
for scope in [Scope::Call, Scope::Conversation, Scope::Workspace] {
rt(Gesture::Act(Action::AnswerHold {
workspace: "ws".to_owned(),
agent: "c-1".to_owned(),
answer: Answer { ruling, scope },
}));
}
}
let bad = parse("/answer maybe", &ctx()).expect_err("only the three words");
assert!(bad.contains("unknown verdict"), "{bad}");
let bare = parse("/answer", &ctx()).expect_err("no default verdict exists");
assert!(bare.contains("pass, hold or refuse"), "{bare}");
let scope = parse("/answer pass --scope everywhere", &ctx()).expect_err("three scopes");
assert!(scope.contains("unknown scope"), "{scope}");
assert_eq!(
parse("/answer pass", &ctx()).expect("the call alone"),
Gesture::Act(Action::AnswerHold {
workspace: "ws".to_owned(),
agent: "c-1".to_owned(),
answer: Answer::once(Ruling::Pass),
})
);
}
#[test]
fn the_capability_floor_round_trips_both_ways_and_takes_no_words() {
for raised in [true, false] {
rt(Gesture::Act(Action::Floor {
workspace: "ws".to_owned(),
agent: "c-1".to_owned(),
raised,
}));
}
let extra = parse("/revoke everything", &ctx()).expect_err("it takes no arguments");
assert!(extra.contains("takes no arguments"), "{extra}");
let unaimed = parse("/restore", &Context::default()).expect_err("nothing is selected");
assert!(unaimed.contains("no workspace in context"), "{unaimed}");
let unselected = parse(
"/restore",
&Context {
workspace: Some("ws".to_owned()),
..Context::default()
},
)
.expect_err("a floor is written for a conversation, and none is selected");
assert!(
unselected.contains("no conversation is selected"),
"{unselected}"
);
}
#[test]
fn every_monitor_action_round_trips() {
rt(Gesture::Act(Action::Monitor(Verb::Arm {
workspace: "ws".to_owned(),
model: "claude-haiku-4-5".to_owned(),
})));
rt(Gesture::Act(Action::Monitor(Verb::Disarm {
workspace: "ws".to_owned(),
})));
rt(Gesture::Act(Action::Monitor(Verb::Flag {
workspace: "ws".to_owned(),
agent: "c-1".to_owned(),
reason: "it is rewriting an unrelated crate".to_owned(),
})));
rt(Gesture::Ask(Query::Workspaces));
}
#[test]
fn every_fleet_action_round_trips() {
rt(Gesture::Act(Action::Fleet(crate::fleet::Verb::Arm {
workspace: "ws".to_owned(),
project: "proj".to_owned(),
cap: 4,
})));
rt(Gesture::Act(Action::Fleet(crate::fleet::Verb::Disarm {
workspace: "ws".to_owned(),
})));
rt(Gesture::Ask(Query::Conversations {
workspace: "ws".to_owned(),
}));
rt(Gesture::Ask(Query::Balls));
rt(Gesture::Ask(Query::Board));
rt(Gesture::Ask(Query::Attention));
rt(Gesture::Ask(Query::Ops { max: 12 }));
for text in ["", "tekeli-li"] {
rt(Gesture::Ask(Query::Search {
text: text.to_owned(),
}));
}
for file in [
None,
Some(crate::workdiff::WorkFile {
ball: "bl-1".to_owned(),
handle: None,
path: "src/a.rs".to_owned(),
}),
Some(crate::workdiff::WorkFile {
ball: "bl-1".to_owned(),
handle: Some("at-1a2b3c4d".to_owned()),
path: "src/a.rs".to_owned(),
}),
] {
rt(Gesture::Ask(Query::WorkDiff {
workspace: "ws".to_owned(),
file,
}));
}
rt(Gesture::Ask(Query::Science {
workspace: "ws".to_owned(),
}));
rt(Gesture::Ask(Query::Help { verb: None }));
rt(Gesture::Ask(Query::Help {
verb: Some("prepare".to_owned()),
}));
}
#[test]
fn every_spend_gesture_round_trips_from_an_empty_seat() {
use crate::spend::Price;
let bare = Context::default();
let gestures = [
Gesture::Act(Action::Price {
provider: "anthropic".to_owned(),
model: "opus".to_owned(),
rates: Some(Price {
input: 15_000_000,
output: 75_000_000,
cache_read: 1_500_000,
cache_write: 18_750_000,
}),
}),
Gesture::Act(Action::Price {
provider: "claude-session-direct".to_owned(),
model: crate::spend::ANY.to_owned(),
rates: Some(Price::default()),
}),
Gesture::Act(Action::Price {
provider: "anthropic".to_owned(),
model: "opus".to_owned(),
rates: None,
}),
Gesture::Act(Action::Ceiling {
micro_usd: Some(12_500_000),
}),
Gesture::Act(Action::Ceiling { micro_usd: None }),
Gesture::Ask(Query::Prices),
];
for gesture in gestures {
rt(gesture.clone());
let line = crate::boundary::line::spell(&gesture);
assert_eq!(parse(&line, &bare), Ok(gesture), "via {line} at no seat");
}
assert_eq!(
parse("/price anthropic opus 15 75", &bare),
Ok(Gesture::Act(Action::Price {
provider: "anthropic".to_owned(),
model: "opus".to_owned(),
rates: Some(Price {
input: 15_000_000,
output: 75_000_000,
cache_read: 0,
cache_write: 0,
}),
}))
);
assert_eq!(
parse("/price anthropic opus 15 75 1.5", &bare),
Ok(Gesture::Act(Action::Price {
provider: "anthropic".to_owned(),
model: "opus".to_owned(),
rates: Some(Price {
input: 15_000_000,
output: 75_000_000,
cache_read: 1_500_000,
cache_write: 0,
}),
}))
);
}