use super::super::Stream;
use super::super::verdict::REFUSED;
use super::said;
#[test]
fn a_misspelled_enrollment_earns_the_row_s_usage() {
let said = said(&["enroll", "home"]);
assert_eq!(said.code, 2);
assert!(
said.text.contains(&crate::verbs::ENROLL.usage()),
"{}",
said.text
);
}
#[test]
fn the_start_word_refuses_by_arity_and_says_what_it_takes() {
for words in [vec!["start"], vec!["start", "home"]] {
let v = said(&words);
assert_eq!(v.code, REFUSED, "{words:?}");
assert_eq!(v.stream, Stream::Err);
let wanted = format!(
"`lernie start` takes 2 to 5 argument(s) and got {} — usage: {}",
words.len() - 1,
crate::verbs::doors::START.usage()
);
assert!(v.text.contains(&wanted), "{}", v.text);
}
}
#[test]
fn an_unquoted_goal_is_told_to_quote_it_and_shown_the_whole_grammar() {
let v = said(&["start", "home", "do", "the", "thing"]);
assert_eq!(v.code, REFUSED);
assert_eq!(v.stream, Stream::Err);
assert!(v.text.contains("\"the thing\""), "{}", v.text);
assert!(v.text.contains("ONE argument"), "{}", v.text);
assert!(v.text.contains("--role"), "{}", v.text);
assert!(
v.text.contains(&crate::verbs::doors::START.usage()),
"{}",
v.text
);
}
#[test]
fn a_verb_with_the_wrong_arity_refuses_by_name_and_teaches_the_grammar() {
let v = said(&["transcript", "home"]);
assert_eq!(v.code, REFUSED);
assert!(v.text.contains("`lernie transcript`"), "{}", v.text);
assert!(
v.text
.contains("usage: lernie transcript <workspace> <agent>"),
"{}",
v.text
);
}
#[test]
fn a_body_that_is_not_a_gesture_is_refused_and_pointed_at_the_page() {
for (body, said_what) in [
("not json at all", "not JSON"),
("[1,2]", "a gesture is a JSON object"),
(r#"{"workspace":"home"}"#, "missing field"),
] {
let v = said(&["ask", body]);
assert_eq!(v.code, REFUSED, "{body}");
assert_eq!(v.stream, Stream::Err);
assert!(v.text.contains(said_what), "{body}: {}", v.text);
assert!(v.text.contains("`lernie help`"), "{body}: {}", v.text);
assert_eq!(v.text.lines().count(), 2, "{body}: {}", v.text);
}
}
#[test]
fn a_door_with_the_wrong_arity_refuses_by_name_and_teaches_the_grammar() {
for (words, expected) in [
(
vec!["ask"],
"`lernie ask` takes 1 argument(s) and got 0 — usage: lernie ask <envelope>",
),
(
vec!["ask", "{}", "twice"],
"`lernie ask` takes 1 argument(s) and got 2 — usage: lernie ask <envelope>",
),
(
vec!["entries", "--now"],
"`lernie entries` takes 0 argument(s) and got 1 — usage: lernie entries",
),
(
vec!["help", "a", "b"],
"`lernie help` takes 0 to 1 argument(s) and got 2 — usage: lernie help [<verb>]",
),
] {
let v = said(&words);
assert_eq!(v.code, REFUSED, "{words:?}");
assert!(v.text.contains(expected), "{words:?}:\n{}", v.text);
}
}
#[test]
fn an_unrecognised_argument_refuses_and_quotes_every_word_of_it() {
let v = said(&["seat", "--ws", "Example"]);
assert_eq!(v.code, REFUSED);
assert!(
v.text.contains("unrecognised argument: seat --ws Example"),
"{}",
v.text
);
}
#[test]
fn a_recognised_word_with_extra_words_is_not_recognised() {
let extra = ["--version", "--now"];
let v = said(&extra);
assert_eq!(v.code, REFUSED);
assert!(
v.text
.contains(&format!("unrecognised argument: {}", extra.join(" "))),
"{}",
v.text
);
}
#[test]
fn a_grade_that_is_not_one_of_the_two_refuses_before_anything_is_dialled() {
for typo in ["notagrade", "OPERATOR", ""] {
let v = said(&["enroll", "home", "phone-1", typo]);
assert_eq!(v.code, REFUSED, "{typo:?}");
assert!(
v.text
.contains(&format!("unknown grade {typo:?} — `lernie enroll` takes")),
"{typo:?}: {}",
v.text
);
for word in crate::ui::Grade::both() {
assert!(
v.text.contains(&format!("{:?}", word.word())),
"it names both words that would have worked: {}",
v.text
);
}
}
}
#[test]
fn no_sentence_this_binary_composes_carries_a_run_of_spaces() {
let mut said: Vec<String> = Vec::new();
for verb in crate::verbs::table() {
said.push(verb.usage());
said.push(crate::verbs::help::page(verb.word).unwrap_or_default());
if let Err(refusal) = verb.envelope(Vec::new()) {
said.push(refusal);
}
}
for door in crate::verbs::doors::table() {
said.push(door.usage());
said.push(door.refused(9));
said.push(crate::verbs::help::page(door.word).unwrap_or_default());
}
let run: Vec<String> = said
.iter()
.flat_map(|text| text.lines())
.filter(|line| line.contains(" "))
.map(str::to_owned)
.collect();
assert_eq!(run, Vec::<String>::new());
}
#[test]
fn a_tail_that_is_not_the_flag_is_told_which_word_it_takes() {
let refused = said(&["stop", "home", "Pelican", "kids"]);
assert_eq!(refused.code, REFUSED);
assert!(
refused.text.contains("no word \"kids\""),
"{}",
refused.text
);
assert!(refused.text.contains("\"children\""), "{}", refused.text);
}
#[test]
fn a_trail_depth_that_is_not_a_number_earns_the_door_s_usage() {
let refusal = said(&["ops", "deep"]);
assert_eq!(refusal.code, REFUSED);
assert!(refusal.text.contains(r#"got "deep""#), "{}", refusal.text);
assert!(
refusal.text.contains(&crate::verbs::doors::OPS.usage()),
"{}",
refusal.text
);
}
#[test]
fn a_trailing_json_flag_earns_the_sentence_that_says_where_it_goes() {
for words in [
vec!["workspaces", "--json"],
vec!["entries", "--json"],
vec!["conversations", "home", "--json"],
vec!["ops", "--json"],
] {
let refusal = said(&words);
assert_eq!(refusal.code, REFUSED, "{words:?}");
assert!(
refusal.text.contains("`--json` goes BEFORE the word"),
"{words:?}: {}",
refusal.text
);
}
let plain = said(&["workspaces", "home"]);
assert!(
!plain.text.contains("`--json` goes BEFORE"),
"{}",
plain.text
);
}