use super::super::stream_events::apply_event;
use super::super::transcript::BlockKind;
use super::support::{empty_app, empty_app_with_text, fixed_status, render_buffer, render_cursor};
use saya_agent::AgentEvent;
const INPUT_INNER_WIDTH: usize = 38;
#[test]
fn input_cursor_stays_on_one_row_for_an_exact_width_line() {
let mut app = empty_app();
app.input.set_text("a".repeat(INPUT_INNER_WIDTH));
let (x, _y) = render_cursor(&app, &fixed_status(), 40, 24);
assert_eq!(
x,
1 + INPUT_INNER_WIDTH as u16,
"exact-width line: cursor at the last inner column, not past the border"
);
}
#[test]
fn input_cursor_wraps_to_second_row_when_line_exceeds_width() {
let mut app = empty_app();
app.input.set_text("a".repeat(INPUT_INNER_WIDTH + 1));
let (x, y) = render_cursor(&app, &fixed_status(), 40, 24);
assert_eq!(
x, 2,
"one char over: cursor column is 2 (inner.x=1 + wrapped col=1), not the border"
);
let two = empty_app_with_text(&"a".repeat(INPUT_INNER_WIDTH + 1));
let one = empty_app_with_text(&"a".repeat(INPUT_INNER_WIDTH));
assert_eq!(
two.input_rows(40),
2,
"a wrapped line grows the box to 2 visual rows"
);
assert_eq!(
one.input_rows(40),
1,
"an exact-width line keeps the box at 1 visual row"
);
let buffer_two = render_buffer(&two, &fixed_status(), 40, 24);
let wrapped_rows = buffer_two.lines().filter(|l| l.contains("│a")).count();
assert!(
wrapped_rows >= 2,
"the wrapped line shows two content rows, not one truncated row:\n{buffer_two}"
);
let _ = (y, app);
}
#[test]
fn input_empty_buffer_cursor_at_origin() {
let app = empty_app();
let (x, y) = render_cursor(&app, &fixed_status(), 40, 24);
assert_eq!(x, 1, "empty buffer: cursor at inner left");
assert_eq!(y, 22, "empty buffer: cursor at inner top row");
}
#[test]
fn continuation_lines_carry_no_label() {
let mut app = empty_app();
app.transcript.push(
BlockKind::User,
"alpha beta gamma delta epsilon zeta eta theta iota kappa lambda mu",
);
let buffer = render_buffer(&app, &fixed_status(), 40, 24);
let you_rows = buffer
.lines()
.filter(|line| line.trim_start_matches(['"', ' ']).starts_with("YOU"))
.count();
assert_eq!(
you_rows, 1,
"the user turn introduces exactly one YOU label row:\n{buffer}"
);
assert!(
!buffer.contains("❯ "),
"no glyph rail paints anymore:\n{buffer}"
);
}
#[test]
fn episode_first_line_carries_the_shared_label() {
use super::super::run_panel::RunPanel;
use super::super::run_worker::RunWorker;
let expected = super::super::transcript::rows::label(BlockKind::Assistant);
let mut app = empty_app();
app.transcript.push(BlockKind::User, "count the orders");
let (tx, rx) = super::super::run_panel::test_channels();
let mut panel = RunPanel::new(
RunWorker {
rx,
cancel: saya_agent::CancellationToken::new(),
},
"r-label".into(),
"survey".into(),
);
let _ = tx;
apply_event(
&mut panel.episode,
AgentEvent::assistant_text("Step one profiled the tables."),
false,
);
app.run_panel = Some(panel);
let buffer = render_buffer(&app, &fixed_status(), 100, 30);
let word = expected.expect("user turns have a label");
assert!(
buffer.contains(word),
"the episode paints the shared label {word:?}:\n{buffer}"
);
}
#[test]
fn a_failure_is_not_distinguished_by_colour_alone() {
let mut app = empty_app();
app.transcript.push(BlockKind::User, "hi");
app.transcript.push(BlockKind::Error, "boom");
let buffer = render_buffer(&app, &fixed_status(), 80, 24);
assert!(
buffer.contains("✗ boom"),
"a failure keeps a mark that survives with colour stripped:\n{buffer}"
);
assert!(
!buffer.contains("ERROR"),
"and no ERROR label is invented to provide it — the failure headline \
is Phase 7's work:\n{buffer}"
);
}
#[test]
fn system_content_is_not_absorbed_into_the_answer_above_it() {
let mut app = empty_app();
app.transcript
.push(BlockKind::Assistant, "here is the answer");
app.transcript
.push(BlockKind::System, "memory supplied · 1 claim");
let buffer = render_buffer(&app, &fixed_status(), 80, 24);
assert!(
buffer.contains(" here is the answer"),
"the answer body indents under SAYA:\n{buffer}"
);
assert!(
buffer.contains("· memory supplied"),
"the receipt keeps a mark distinguishing it from that answer:\n{buffer}"
);
}
#[test]
fn measured_height_equals_painted_height() {
let mut app = empty_app();
app.transcript.push(BlockKind::User, "hello");
app.transcript
.push(BlockKind::Assistant, "hi there, here is the answer");
let width = 80;
let total = app.transcript.total_lines(width);
let painted = app
.transcript
.wide_view(width, total, &app.wide_table)
.len();
assert_eq!(
total, painted,
"measured height ({total}) must equal painted height ({painted})"
);
}