use crate::interactive::tui::transcript::MAX_BLOCKS;
use crate::interactive::tui::transcript::{BlockKind, Transcript};
const W: usize = 80;
const H: usize = 5;
fn view_texts(t: &Transcript, height: usize) -> Vec<String> {
t.view(W, height)
.iter()
.map(|row| row.text.clone())
.collect()
}
fn write_request(path: &str) -> serde_json::Value {
serde_json::json!({"path": path})
}
fn scrolled_history() -> Transcript {
let mut t = Transcript::new();
for i in 0..30 {
t.push(BlockKind::Assistant, format!("history line {i}"));
}
t.scroll_up(30, W, H);
assert!(
!t.is_following_tail(),
"precondition: the reader scrolled up"
);
assert_eq!(
view_texts(&t, H),
[
"history line 12",
"SAYA",
"history line 13",
"SAYA",
"history line 14",
],
"precondition: the window shows painted rows 25…29"
);
t
}
#[test]
fn a_completion_is_visible_on_the_very_next_render() {
let mut t = Transcript::new();
t.push(BlockKind::User, "write the note");
t.buffer_tool_request("workspace_write".into(), write_request("notes.md"), None);
let first = view_texts(&t, 10);
assert!(
first
.iter()
.any(|l| l.contains("→ workspace_write: notes.md")),
"precondition: the request is painted: {first:?}"
);
assert!(
t.buffer_tool_completion("workspace_write", "notes.md written"),
"precondition: the completion pairs"
);
let second = view_texts(&t, 10);
assert!(
second
.iter()
.any(|l| l.contains("✓ workspace_write: notes.md written")),
"the completion must be painted on the very next render: {second:?}"
);
}
#[test]
fn a_failure_mark_is_visible_on_the_very_next_render() {
let mut t = Transcript::new();
t.push(BlockKind::User, "write the note");
t.buffer_tool_request("workspace_write".into(), write_request("notes.md"), None);
let _ = view_texts(&t, 10);
assert!(
t.buffer_tool_completion("workspace_write", "write failed: disk full"),
"precondition: the completion pairs"
);
let second = view_texts(&t, 10);
assert!(
second
.iter()
.any(|l| l.contains("✗ workspace_write: write failed")),
"the failure mark must be painted on the very next render: {second:?}"
);
}
#[test]
fn a_tool_request_while_scrolled_does_not_move_the_viewport() {
let mut t = scrolled_history();
let before = view_texts(&t, H);
t.buffer_tool_request("workspace_write".into(), write_request("notes.md"), None);
assert!(
t.blocks()
.last()
.is_some_and(|b| b.text.contains("→ workspace_write")),
"precondition: the request landed below the fold"
);
assert_eq!(
view_texts(&t, H),
before,
"a tool request while scrolled must not move the visible window: \
history 25…29 must not become 27…31"
);
}
#[test]
fn a_tool_request_while_scrolled_counts_as_unseen_activity() {
let mut t = scrolled_history();
assert_eq!(
t.unseen_new_rows(),
0,
"precondition: nothing has arrived below yet"
);
t.buffer_tool_request("workspace_write".into(), write_request("notes.md"), None);
assert!(
t.unseen_new_rows() > 0,
"a request below a scrolled reader must raise the count through the \
one raise site"
);
}
#[test]
fn same_name_parallel_calls_still_pair_fifo() {
let mut t = Transcript::new();
t.push(BlockKind::User, "write two notes");
t.buffer_tool_request("workspace_write".into(), write_request("north.md"), None);
t.buffer_tool_request("workspace_write".into(), write_request("south.md"), None);
assert!(t.buffer_tool_completion("workspace_write", "north.md written"));
assert_eq!(
t.open_tool_count(),
1,
"the first completion closed the oldest open call, not the newest"
);
assert!(t.buffer_tool_completion("workspace_write", "south.md written"));
assert_eq!(t.open_tool_count(), 0);
t.flush_tool_buffer(
|name, args| vec![format!("→ {name} {args}")],
|name, summary| format!("✓ {name}: {summary}"),
);
let text = t
.blocks()
.iter()
.flat_map(|b| {
let mut v = vec![b.text.clone()];
if let Some(g) = &b.group {
v.extend(g.detail.clone());
}
v
})
.collect::<Vec<_>>()
.join("\n");
let north_q = text
.find(r#"→ workspace_write {"path":"north.md""#)
.expect("north request");
let north_r = text
.find("✓ workspace_write: north.md written")
.expect("north result");
let south_q = text
.find(r#"→ workspace_write {"path":"south.md""#)
.expect("south request");
let south_r = text
.find("✓ workspace_write: south.md written")
.expect("south result");
assert!(
north_q < north_r && north_r < south_q && south_q < south_r,
"each result must follow its own request:\n{text}"
);
}
#[test]
fn bounds_hold_after_a_completion() {
let mut t = Transcript::new();
for i in 0..MAX_BLOCKS {
t.push(BlockKind::User, format!("m{i}"));
}
t.buffer_tool_request("workspace_write".into(), write_request("notes.md"), None);
assert!(
t.blocks().len() <= MAX_BLOCKS,
"precondition: the request path respects the bound"
);
t.buffer_tool_completion("workspace_write", "notes.md written");
assert!(
t.blocks().len() <= MAX_BLOCKS,
"MAX_BLOCKS must hold after a completion too"
);
}