use super::*;
#[test]
fn role_caps_and_head_fractions_are_asymmetric() {
assert_eq!(Role::User.cap(), 600);
assert_eq!(Role::Assistant.cap(), 900);
assert!(Role::Assistant.cap() > Role::User.cap());
assert!(Role::Assistant.head_frac() > Role::User.head_frac());
assert_eq!(Role::User.label(), "user");
assert_eq!(Role::Assistant.label(), "assistant");
}
#[test]
fn sub_cap_unit_renders_verbatim_no_marker() {
let u = unit(Role::User, 10, "café🛠a short ask", 0);
let r = render_unit_body(&u, None);
assert!(!r.truncated);
assert_eq!(r.body, "café🛠a short ask");
assert_eq!(r.elided_chars, 0);
assert_eq!(r.elided_lines, 0);
assert_eq!(r.rendered_chars, "café🛠a short ask".chars().count());
}
#[test]
fn user_ellipsis_head_360_tail_240_with_counts() {
let body: String = "a".repeat(1000);
let u = unit(Role::User, 10, &body, 3);
let r = render_unit_body(&u, None);
assert!(r.truncated);
assert_eq!(r.elided_chars, 1000 - 600);
assert_eq!(r.elided_lines, 2);
assert!(r.body.starts_with(&"a".repeat(360)));
assert!(r.body.ends_with(&"a".repeat(240)));
assert!(r.body.contains("[+400 chars, 2 lines elided]"));
assert_eq!(r.rendered_chars, 600);
}
#[test]
fn assistant_ellipsis_head_larger_than_user_head() {
let body: String = "b".repeat(2000);
let a = unit(Role::Assistant, 20, &body, 7);
let r = render_unit_body(&a, None);
assert!(r.truncated);
assert_eq!(r.elided_chars, 2000 - 900);
assert!(r.body.starts_with(&"b".repeat(594)));
assert!(r.body.ends_with(&"b".repeat(306)));
assert!(r.body.contains("[+1100 chars, 4 lines elided]"));
assert_eq!(r.rendered_chars, 900);
let ubody: String = "u".repeat(2000);
let ru = render_unit_body(&unit(Role::User, 1, &ubody, 0), None);
let asst_head_len = 594usize;
let user_head_len = 360usize;
assert!(asst_head_len > user_head_len);
assert!(ru.body.starts_with(&"u".repeat(360)));
}
#[test]
fn lines_elided_counts_only_the_newlines_the_cut_removed() {
let body: String = "a".repeat(1000);
let u = unit_at(Role::User, 10, &body, &[100, 200, 400, 700, 800]);
let r = render_unit_body(&u, None);
assert!(r.truncated);
assert_eq!(r.elided_chars, 400);
assert_eq!(
r.elided_lines, 2,
"only the newlines strictly inside [360,760) are elided: {}",
r.body
);
assert!(
r.body.contains("[+400 chars, 2 lines elided]"),
"{}",
r.body
);
let uncut = render_unit_body(&u, Some(1000));
assert!(!uncut.truncated);
assert_eq!(uncut.elided_lines, 0);
assert!(!uncut.body.contains("lines elided"), "{}", uncut.body);
let wider = render_unit_body(&u, Some(300));
assert_eq!(wider.elided_chars, 700);
assert_eq!(wider.elided_lines, 4, "{}", wider.body);
}
#[test]
fn elided_newlines_counts_the_half_open_span() {
let at = [10u32, 20, 30, 40];
assert_eq!(elided_newlines(&at, 20, 40), 2, "20 in, 40 out");
assert_eq!(elided_newlines(&at, 0, 41), 4);
assert_eq!(elided_newlines(&at, 41, 99), 0);
assert_eq!(elided_newlines(&[15, 15, 15], 10, 20), 3);
assert_eq!(elided_newlines(&at, 20, 20), 0);
assert_eq!(elided_newlines(&at, 40, 20), 0);
assert_eq!(elided_newlines(&[], 0, 99), 0);
}
#[test]
fn single_line_user_omits_lines_elided_note() {
let body: String = "x".repeat(1000);
let u = unit(Role::User, 5, &body, 0); let r = render_unit_body(&u, None);
assert!(r.truncated);
assert!(r.body.contains("[+400 chars]"));
assert!(
!r.body.contains("lines elided"),
"single-line message must omit the line note: {}",
r.body
);
}
#[test]
fn ellipsis_cut_is_codepoint_safe_for_multibyte_token() {
let mut body = String::new();
body.push_str(&"a".repeat(360)); body.push('🛠'); body.push_str(&"a".repeat(400));
let u = unit(Role::User, 1, &body, 0);
let r = render_unit_body(&u, None);
assert!(r.body.starts_with(&"a".repeat(360)));
assert!(!r.body.contains('\u{FFFD}'), "no replacement char");
let rebuilt: String = r.body.chars().collect();
assert_eq!(rebuilt, r.body);
}
#[test]
fn emoji_in_tail_is_wholly_kept() {
let mut body = String::new();
body.push_str(&"a".repeat(700));
body.push('🛠');
body.push_str(&"b".repeat(239)); let u = unit(Role::User, 1, &body, 0);
let r = render_unit_body(&u, None);
assert!(r.truncated);
assert!(r.body.contains('🛠'), "emoji in the kept tail: {}", r.body);
assert!(!r.body.contains('\u{FFFD}'));
}
#[test]
fn render_turn_text_emits_user_marker_assistant_for_both() {
let t = mk_turn(0, Some("the ask"), Some("the reply"), 3, 0);
let mut lines: Vec<String> = Vec::new();
render_turn_text(&t, SelSides::Both, &cfg(), None, &mut |s| lines.push(s));
let joined = lines.join("\n");
assert!(joined.contains("â–½ L1"), "user header: {joined}");
assert!(joined.contains("[3 tool calls]"), "tool marker: {joined}");
assert!(joined.contains("â–³ L5"), "assistant header: {joined}");
assert!(joined.contains("the ask"));
assert!(joined.contains("the reply"));
let mut uonly: Vec<String> = Vec::new();
render_turn_text(&t, SelSides::UserOnly, &cfg(), None, &mut |s| uonly.push(s));
let uj = uonly.join("\n");
assert!(uj.contains("â–½ L1"));
assert!(!uj.contains("tool calls"), "no marker on user-only: {uj}");
assert!(!uj.contains("â–³ L5"), "no assistant on user-only");
let mut aonly: Vec<String> = Vec::new();
render_turn_text(&t, SelSides::AssistantOnly, &cfg(), None, &mut |s| {
aonly.push(s)
});
let aj = aonly.join("\n");
assert!(!aj.contains("â–½ L1"), "no user on assistant-only");
assert!(aj.contains("â–³ L5"));
}
#[test]
fn render_turn_text_zero_tool_turn_omits_marker() {
let t = mk_turn(0, Some("ask"), Some("reply"), 0, 0);
let mut lines: Vec<String> = Vec::new();
render_turn_text(&t, SelSides::Both, &cfg(), None, &mut |s| lines.push(s));
assert!(
!lines.join("\n").contains("tool calls"),
"0-tool omits marker"
);
}
#[test]
fn emit_unit_text_flags_dedup_unit() {
let mut u = unit(Role::User, 7, "deduped ask", 0);
u.also_in_summary = true;
let mut lines: Vec<String> = Vec::new();
emit_unit_text(&u, None, &mut |s| lines.push(s));
assert!(
lines.iter().any(|l| l.contains("(also in summary)")),
"dedup flag rendered: {lines:?}"
);
assert!(lines[0].starts_with("â–½ L7"), "user glyph: {lines:?}");
}
#[test]
fn render_turn_text_user_only_with_no_user_emits_nothing() {
let t = mk_turn(0, None, Some("only assistant"), 0, 0);
let mut lines: Vec<String> = Vec::new();
render_turn_text(&t, SelSides::UserOnly, &cfg(), None, &mut |s| lines.push(s));
assert!(lines.is_empty(), "no user to render: {lines:?}");
}
#[test]
fn render_turn_text_assistant_only_with_no_assistant_emits_nothing() {
let t = mk_turn(0, Some("only user"), None, 0, 0);
let mut lines: Vec<String> = Vec::new();
render_turn_text(&t, SelSides::AssistantOnly, &cfg(), None, &mut |s| {
lines.push(s)
});
assert!(lines.is_empty(), "no assistant to render: {lines:?}");
}
#[test]
fn render_turn_text_both_with_zero_tools_no_marker_line() {
let t = mk_turn(0, Some("ask"), Some("reply"), 0, 0);
let mut lines: Vec<String> = Vec::new();
render_turn_text(&t, SelSides::Both, &cfg(), None, &mut |s| lines.push(s));
assert!(lines.iter().any(|l| l.starts_with("â–½ L")));
assert!(lines.iter().any(|l| l.starts_with("â–³ L")));
assert!(!lines.iter().any(|l| l.contains("tool calls")));
}
#[test]
fn emit_unit_text_non_dup_has_no_flag() {
let u = unit(Role::User, 3, "a normal ask", 0);
let mut lines: Vec<String> = Vec::new();
emit_unit_text(&u, None, &mut |s| lines.push(s));
assert!(!lines.iter().any(|l| l.contains("also in summary")));
assert!(lines[0].starts_with("â–½ L3"));
}
fn span(
messages: usize,
tool_calls: usize,
failed: usize,
first_line: usize,
last_line: usize,
chars: usize,
) -> PlaceholderSpan {
PlaceholderSpan {
messages,
tool_calls,
failed,
first_line,
last_line,
chars,
previews: Vec::new(),
}
}
#[test]
fn agent_placeholder_line_pluralizes_each_noun_independently() {
let one = span(1, 0, 0, 42, 42, 137);
assert_eq!(
agent_placeholder_line(&one),
"â–³ L42 [1 agent message collapsed, 137 chars, 0 tool calls]"
);
let many = span(3, 4, 2, 10, 20, 512);
assert_eq!(
agent_placeholder_line(&many),
"△ L10–L20 [3 agent messages collapsed, 512 chars, 4 tool calls, 2 failed]"
);
let one_fail = span(2, 1, 1, 5, 9, 64);
assert_eq!(
agent_placeholder_line(&one_fail),
"△ L5–L9 [2 agent messages collapsed, 64 chars, 1 tool call, 1 failed]"
);
}
#[test]
fn a_fold_previews_only_its_substantive_members() {
let under = unit(Role::Assistant, 11, &body_chars("SHORT", 209), 0);
assert_eq!(under.full_chars, 209);
assert!(collapsed_preview(&under).is_none());
let over = unit(Role::Assistant, 12, &body_chars("LONG", 210), 0);
assert_eq!(over.full_chars, 210);
let p = collapsed_preview(&over).expect("a preview at the threshold");
assert_eq!(p.line, 12);
assert_eq!(
p.excerpt.chars().count(),
60 + "… (+150 chars)".chars().count()
);
assert!(p.excerpt.contains("… (+150 chars)"), "{}", p.excerpt);
assert_eq!(
collapsed_preview_line(&p),
format!(" L12 {}", p.excerpt)
);
}
#[test]
fn a_fold_marker_and_its_preview_lines_are_what_the_cost_charges() {
let mut s = span(2, 3, 0, 7, 9, 430);
s.previews = vec![
CollapsedPreview {
line: 7,
excerpt: "first collapsed head".to_string(),
},
CollapsedPreview {
line: 9,
excerpt: "second collapsed head".to_string(),
},
];
let lines = agent_placeholder_lines(&s);
assert_eq!(lines.len(), 3, "marker + two previews: {lines:?}");
assert_eq!(lines[0], agent_placeholder_line(&s));
assert_eq!(lines[1], " L7 first collapsed head");
assert_eq!(lines[2], " L9 second collapsed head");
let emitted: usize = lines.iter().map(|l| l.chars().count() + 1).sum();
assert_eq!(agent_placeholder_cost(&s), emitted);
let bare = span(2, 3, 0, 7, 9, 430);
assert_eq!(
agent_placeholder_cost(&bare),
agent_placeholder_line(&bare).chars().count() + 1
);
}
#[test]
fn placeholder_attribution_sums_per_message_tool_and_failed() {
let mut agents = vec![
agent_msg(10, "found the cause", 0, 0), agent_msg(20, "let me a", 2, 1), agent_msg(30, "let me b", 3, 0), agent_msg(40, "let me c", 1, 2), agent_msg(50, "let me d", 0, 0), agent_msg(60, "let me e", 0, 0), agent_msg(70, "let me f", 0, 0), agent_msg(80, "done", 0, 0), ];
assign_positions(&mut agents);
let t = TurnSlice {
turn_index: 0,
user: Some(unit(Role::User, 1, "ask", 0)),
tool_calls: 6,
image_ids: Vec::new(),
agents,
compactions_before: 0,
is_automation: false,
automation: None,
};
let lane = select_agent_messages(&t, &rich_cfg());
let span = lane
.iter()
.find_map(|r| match r {
AgentRender::Placeholder(s) => Some(s),
_ => None,
})
.expect("a placeholder for the collapsed middles");
assert_eq!(span.messages, 6, "six middle declarations");
assert_eq!(
span.tool_calls,
2 + 3 + 1,
"Y sums the span's preceding tool calls"
);
assert_eq!(
span.failed,
1 + 2,
"Z sums the span's erroring tool results"
);
assert_eq!(span.first_line, 20);
assert_eq!(span.last_line, 70);
assert_eq!(span.chars, 6 * "let me a".chars().count());
assert!(span.previews.is_empty(), "{:?}", span.previews);
}