use super::fixture::world;
use super::painted;
use crate::cli_outbound::Cli;
use crate::opslog::{OpEntry, Origin};
const CONFIG_DECLINE: &str = "lernie prompt: provider error (Config) on provider row \
\"yogdrive-no-such-provider\": unknown provider `yogdrive-no-such-provider`";
const REASON_NEEDLE: &str = "unknown provider `yogdrive-no-such-provider`";
const REMEDY_NEEDLE: &str = "no provider row named yogdrive-no-such-provider";
const CONFIG_TAB: &str = "Config";
fn says(text: &str, needle: &str) -> usize {
text.matches(needle).count()
}
fn dead_dispatch(world: &mut super::fixture::World, stderr: &str) {
let row = OpEntry::synthetic_failure(
"T0".to_owned(),
vec!["lernie".to_owned(), "prompt".to_owned()],
"ws".to_owned(),
stderr.to_owned(),
Origin::Conversation,
);
crate::opslog::append(world.model.state_root(), &row).unwrap();
world.model.after_lernie_verb();
world.converge();
let ws = world.ws.clone();
world.model.focus_agent(&ws, "c-1");
world.converge();
}
#[test]
fn a_config_kind_dispatch_failure_paints_its_reason_and_the_way_out() {
let (lernie, bl) = (Cli::new("lernie"), Cli::new("bl"));
let mut shell = world();
dead_dispatch(&mut shell, CONFIG_DECLINE);
let text = painted(&mut shell, &lernie, &bl);
assert!(
text.contains(REASON_NEEDLE),
"the decline's own words reach the glass:\n{text}"
);
assert!(
text.contains(REMEDY_NEEDLE),
"THE BALL: the remedy beside the reason, naming the row:\n{text}"
);
assert!(
text.contains("config.toml"),
"and the file it has to be added to:\n{text}"
);
let mut bare = super::fixture::world();
dead_dispatch(&mut bare, "lernie prompt: exit 1");
let baseline = painted(&mut bare, &lernie, &bl);
assert!(
says(&text, CONFIG_TAB) > says(&baseline, CONFIG_TAB),
"the route to the §9.1 editor is a control, not only a sentence:\n{text}"
);
assert!(
text.contains(crate::opslog::operator::ACK_LABEL),
"Dismiss stays where it was:\n{text}"
);
}
#[test]
fn a_failure_of_another_class_earns_the_reason_and_no_config_route() {
let (lernie, bl) = (Cli::new("lernie"), Cli::new("bl"));
let mut world = world();
dead_dispatch(
&mut world,
"lernie prompt: provider error (Transport) on provider row \
\"openai-chatgpt\": connection reset by peer",
);
let text = painted(&mut world, &lernie, &bl);
assert!(
text.contains("connection reset by peer"),
"the reason still renders as itself:\n{text}"
);
assert!(
!text.contains(REMEDY_NEEDLE),
"and claims no config remedy it cannot honour:\n{text}"
);
assert!(
text.contains(crate::opslog::operator::ACK_LABEL),
"Dismiss is what this class has:\n{text}"
);
}
#[test]
fn an_auth_kind_step_failure_paints_its_reason_and_offers_the_sign_in() {
let (lernie, bl) = (Cli::new("lernie"), Cli::new("bl"));
let mut world = world();
let ws = world.ws.clone();
world.model.focus_agent(&ws, "c-1");
world.converge();
let text = painted(&mut world, &lernie, &bl);
assert!(
text.contains("failed on") && text.contains("credentials"),
"the §13.3 derived state, in words:\n{text}"
);
assert!(
text.contains("Login"),
"THE BALL: the sign-in is one control away from the dead turn:\n{text}"
);
assert!(
!text.contains(REMEDY_NEEDLE),
"an auth fault is not a config fault:\n{text}"
);
}