mod gap;
use super::super::fixture::{World, seed_world};
use super::super::screen::Screen;
use super::harness::{MINTED_FIRST, SAID, quick, rows_named, say, shot, typed, world};
use crate::cli_outbound::Cli;
use std::os::unix::fs::PermissionsExt;
use std::path::Path;
use tempfile::tempdir;
pub(super) const AGAIN: &str = "and hurry";
pub(super) const LANDED: &str = "c-2";
fn recording_lernie(dir: &Path, log: &Path) -> Cli {
let path = dir.join("lernie");
std::fs::write(
&path,
format!(
"#!/bin/sh\nprintf '%s\\n' \"$*\" >> '{}'\ncase \"$1\" in\n{}esac\nexit 0\n",
log.display(),
crate::test_support::authoring_new_arm()
),
)
.unwrap();
let mut perms = std::fs::metadata(&path).unwrap().permissions();
perms.set_mode(0o755);
std::fs::set_permissions(&path, perms).unwrap();
Cli::new(path)
}
pub(super) fn verbs(log: &Path) -> Vec<String> {
lines(log)
.into_iter()
.filter_map(|line| line.split_whitespace().next().map(str::to_owned))
.collect()
}
pub(super) fn lines(log: &Path) -> Vec<String> {
std::fs::read_to_string(log)
.unwrap_or_default()
.lines()
.map(str::to_owned)
.collect()
}
pub(super) fn armed(bin: &Path, log: &Path) -> (World, Screen) {
let mut world = quick(world());
seed_world(&world);
let screen = Screen::with_lernie(recording_lernie(bin, log));
assert!(screen.idle(&mut world), "the cursor starts in the composer");
(world, screen)
}
pub(super) fn driver_writes(world: &mut World) {
world.add_root(LANDED, MINTED_FIRST);
let ws = world.ws.clone();
world.model.mark_dirty([ws]);
world.converge();
}
fn queue_rows(out: &egui::FullOutput) -> Vec<String> {
crate::paint_probe::painted_of(out)
.into_iter()
.filter(|(text, _)| text.starts_with('✉'))
.map(|(text, _)| text)
.collect()
}
#[test]
fn a_second_enter_in_the_window_is_held_and_delivered_when_the_start_resolves() {
let bin = tempdir().unwrap();
let log = bin.path().join("verbs.log");
let (mut world, screen) = armed(bin.path(), &log);
say(&screen, &mut world);
assert_eq!(
world.model.focus().agent.as_deref(),
Some(MINTED_FIRST),
"the start selected what it started, by the minted name"
);
assert_eq!(verbs(&log), ["prompt"], "one fire: {:?}", lines(&log));
typed(&screen, &mut world, AGAIN);
assert_eq!(
verbs(&log),
["prompt"],
"nothing was fired at a name that resolves nowhere: {:?}",
lines(&log)
);
let pending = rows_named(&world, MINTED_FIRST);
assert_eq!(pending.len(), 1, "still exactly one conversation");
assert_eq!(
pending[0].root_id, MINTED_FIRST,
"and one name — the mint was not spent twice"
);
let waiting = shot(&screen, &mut world);
assert_eq!(
queue_rows(&waiting).len(),
2,
"both sends are in the queue above the box, in the operator's own words"
);
driver_writes(&mut world);
shot(&screen, &mut world);
assert_eq!(
verbs(&log),
["prompt", "message"],
"the held send left exactly once, after the start: {:?}",
lines(&log)
);
let sent = lines(&log);
assert!(
sent[1].contains(AGAIN) && sent[1].contains(LANDED),
"addressed by the id its branch brought, carrying what was typed: {sent:?}"
);
assert!(
!sent[1].contains(MINTED_FIRST),
"and never by the minted name, which is the whole of the fix: {sent:?}"
);
}
#[test]
fn the_queue_carries_the_started_conversations_first_message_at_once() {
let bin = tempdir().unwrap();
let log = bin.path().join("verbs.log");
let (mut world, screen) = armed(bin.path(), &log);
world.model.select_tab(crate::keymap::InspectorTab::Steps);
assert!(
queue_rows(&shot(&screen, &mut world)).is_empty(),
"nothing is queued before the operator says anything"
);
say(&screen, &mut world);
let out = shot(&screen, &mut world);
assert_eq!(
queue_rows(&out).len(),
1,
"one row, for the one thing that has been said"
);
assert!(
crate::paint_probe::painted_of(&out)
.iter()
.any(|(text, _)| text.contains(SAID)),
"and it says what was typed"
);
assert!(
super::harness::fills(&out).contains(&super::harness::faded_user()),
"faded: yog's own word for it, not yet a statement (§11)"
);
}
#[test]
fn a_healthy_window_paints_no_refusal_and_offers_no_stop() {
let bin = tempdir().unwrap();
let log = bin.path().join("verbs.log");
let (mut world, screen) = armed(bin.path(), &log);
say(&screen, &mut world);
for tab in [
crate::keymap::InspectorTab::Transcript,
crate::keymap::InspectorTab::Steps,
crate::keymap::InspectorTab::Inbox,
crate::keymap::InspectorTab::Files,
crate::keymap::InspectorTab::Config,
] {
world.model.select_tab(tab);
let out = shot(&screen, &mut world);
let said: Vec<String> = crate::paint_probe::painted_of(&out)
.into_iter()
.map(|(text, _)| text)
.filter(|text| text.contains("unknown conversation") || text.contains("ambiguous"))
.collect();
assert!(
said.is_empty(),
"{tab:?} told the operator their own new conversation was unknown: {said:?}"
);
}
let row = rows_named(&world, MINTED_FIRST);
assert!(
!row[0].stoppable,
"no driver has been observed, so there is none to offer to kill"
);
assert!(
row[0].uncertain,
"and yog says so: the state is a framing-only reading, flagged"
);
}