use super::*;
#[test]
fn one_long_line_cannot_flood_the_row_budget() {
const COLS: usize = 80;
let long = "x".repeat(400);
let out = spill_view_lines(&long, 3, COLS);
let rendered_rows: usize = out.iter().map(|l| wrap_to_width(l, COLS).len()).sum();
assert!(
rendered_rows <= 5,
"a single {}-char line rendered {rendered_rows} rows at {COLS} columns \
against a 3-row budget:\n{out:#?}",
long.len()
);
}
#[test]
fn a_line_wider_than_the_terminal_is_measured_but_not_rewrapped() {
const COLS: usize = 40;
let wide = "artifact:abc: returned 44 of 44 body characters at offset 0 (complete)";
assert!(wide.len() > COLS, "the fixture must exceed the width");
let out = spill_view_lines(&format!("first\n{wide}"), 3, COLS);
let joined = out.join("\n");
assert!(
joined.contains("returned 44 of 44 body characters"),
"the phrase was split across a wrap — copy-paste and search are \
broken by rewrapping the canonical block:\n{joined}"
);
}
#[test]
fn a_wide_line_spends_the_row_budget_it_actually_occupies() {
const COLS: usize = 20;
let wide = "w".repeat(50);
let out = spill_view_lines(&format!("a\nb\nc\n{wide}"), 3, COLS);
let body: Vec<&String> = out
.iter()
.filter(|l| l.starts_with('▒') || l.starts_with('▓'))
.collect();
assert_eq!(
body,
vec!["▒ a", &format!("▓ {wide}")],
"the wide line's 3-row cost must still exclude b/c — only the \
1-row head (a) and the wide tail fit the split budget:\n{out:#?}"
);
assert!(out.iter().any(|l| l.contains("lines hidden")), "{out:#?}");
assert!(
out.iter().any(|l| l.contains(SPILL_RECOVERY_HINT)),
"every truncation marker names the way out:\n{out:#?}"
);
}
#[test]
fn spill_view_shows_both_ends_with_gutter_glyphs() {
let small = spill_view_lines("a\nb\nc", 3, 80);
assert_eq!(small, vec!["▒ a", "▒ b", "▒ c", "…"]);
let big = spill_view_lines("l1\nl2\nl3\nl4\nl5", 3, 80);
assert_eq!(
big,
vec![
"▒ l1",
"▲ 3 lines hidden [/spill N raises this view]",
"▓ l5",
"…"
]
);
assert_eq!(spill_view_lines("x\ny", 0, 80), vec!["x", "y"]);
assert!(spill_view_lines("", 3, 80).is_empty());
}
#[test]
fn completed_tool_output_uses_the_spill_view() {
let output = "l1\nl2\nl3\nl4\nl5";
assert_eq!(
spill_view_lines(output, 3, 80),
vec![
"▒ l1",
"▲ 3 lines hidden [/spill N raises this view]",
"▓ l5",
"…"
]
);
let raw: Vec<String> = output.lines().map(str::to_string).collect();
assert_eq!(spill_view_lines(output, 0, 80), raw);
}
#[test]
fn completed_excerpt_names_its_recovery_path() {
let lines = spill_view_lines("l1\nl2\nl3\nl4\nl5", 3, 80);
let hint_lines: Vec<&String> = lines
.iter()
.filter(|l| l.contains("/spill N raises this view"))
.collect();
assert_eq!(
hint_lines.len(),
1,
"the boundary marker must carry the recovery hint exactly once: {lines:?}"
);
assert_eq!(lines.last().map(String::as_str), Some("…"));
let small = spill_view_lines("a\nb", 3, 80);
assert_eq!(small.last().map(String::as_str), Some("…"));
}
#[test]
fn results_then_a_crash_shows_the_last_result_and_the_traceback_head() {
let output = [
"Response 0: initialize ok",
"Response 1: tools/list ok",
"Traceback (most recent call last):",
"File \"cleanup.py\", line 40, in shutdown",
"File \"cleanup.py\", line 30, in _terminate",
"File \"cleanup.py\", line 20, in _kill",
"File \"cleanup.py\", line 15, in _signal",
"File \"cleanup.py\", line 10, in _reap",
"File \"cleanup.py\", line 8, in _wait",
"File \"cleanup.py\", line 6, in _proc",
"File \"cleanup.py\", line 4, in _pid",
"ProcessLookupError: [Errno 3] No such process",
]
.join("\n");
let output = output.as_str();
let out = spill_view_lines(output, 8, 80);
let joined = out.join("\n");
assert!(
joined.contains("Response 1: tools/list ok"),
"the last result line — the confirming evidence — must not be \
fully hidden inside the fold:\n{out:#?}"
);
assert!(
joined.contains("Traceback (most recent call last):"),
"the traceback's own head must not be fully hidden either — \
showing only its tail (the pre-#1973 behavior) is exactly the \
evidence-inversion this issue is about:\n{out:#?}"
);
assert!(
out.last().is_some_and(|l| l == "…"),
"inert-terminated as always:\n{out:#?}"
);
assert!(
joined.contains("ProcessLookupError"),
"the actual exception must still be visible — this is not a \
head-only regression of the tail:\n{out:#?}"
);
}
#[test]
fn cargo_style_output_with_errors_last_still_shows_the_errors() {
let output = [
"Compiling foo v0.1.0",
"Compiling bar v0.1.0",
"Compiling baz v0.1.0",
"Compiling qux v0.1.0",
"Compiling quux v0.1.0",
"Compiling corge v0.1.0",
"Compiling grault v0.1.0",
"error[E0499]: cannot borrow `x` as mutable more than once",
" --> src/lib.rs:42:5",
"error[E0502]: cannot borrow `x` as immutable",
" --> src/lib.rs:43:5",
"error: could not compile `foo` (bin \"foo\") due to 2 previous errors",
]
.join("\n");
let output = output.as_str();
let out = spill_view_lines(output, 8, 80);
let joined = out.join("\n");
assert!(
joined.contains("error: could not compile `foo` (bin \"foo\") due to 2 previous errors"),
"the final cargo summary line — the decisive content for THIS \
shape — must still be visible:\n{out:#?}"
);
assert!(
joined.contains("E0499") || joined.contains("E0502"),
"at least one of the actual diagnostics should still be in the \
tail window:\n{out:#?}"
);
}
#[test]
fn the_marker_reserve_is_tightened_by_its_own_height() {
for view in [2usize, 3, 5, 8, 12] {
let lines: Vec<String> = (0..view + 20).map(|i| format!("l{i}")).collect();
let out = spill_view_lines(&lines.join("\n"), view, 80);
assert!(
out.len() <= view + 1,
"view={view}: rendered {} total rows, expected at most view+1 \
({}) — the marker's reserve regressed:\n{out:#?}",
out.len(),
view + 1
);
}
}