use std::collections::HashSet;
use super::legible::{SIZES, answer, long, run, seen, whole};
use super::render::{entry, tx};
use crate::transcript::{AutoExpand, Block, Entry, EntryKind, Row, Transcript, Usage, rows};
pub(super) fn mixed(payload: &str) -> Transcript {
tx(vec![
Entry {
name: "001-user.md".into(),
raw: b"x".to_vec(),
kind: EntryKind::Delivered {
sender: "user".into(),
epitaph: None,
body: payload.to_owned(),
},
},
entry(EntryKind::Model {
model_id: "opus".into(),
usage: Usage::default(),
blocks: vec![
Block::Thinking(payload.to_owned()),
Block::ToolUse {
id: "toolu_1".into(),
name: "Read".into(),
input_summary: payload.to_owned(),
},
],
}),
Entry {
name: "003-tool.json".into(),
raw: b"{}".to_vec(),
kind: EntryKind::ToolResult {
tool_use_id: "toolu_1".into(),
content: payload.to_owned(),
is_error: false,
},
},
Entry {
name: "004-opus.json".into(),
raw: b"{}".to_vec(),
kind: EntryKind::Model {
model_id: "opus".into(),
usage: Usage::default(),
blocks: vec![Block::Text(payload.to_owned())],
},
},
])
}
#[test]
fn a_model_answer_arrives_open_behind_a_rolled_up_turn() {
let payload = long();
let t = mixed(&payload);
for (w, h) in SIZES {
let painted = seen(&t, AutoExpand::default(), true, w, h);
assert!(
painted.iter().any(|s| s.text.contains("inference call")),
"the machinery must really roll up, or this proves nothing: {:?}",
painted.iter().map(|s| &s.text).collect::<Vec<_>>()
);
assert!(
painted.iter().filter(|s| s.text == payload).count() >= 1,
"the answer arrives open and entire at {w}x{h}: {:?}",
painted
.iter()
.map(|s| s.text.chars().count())
.collect::<Vec<_>>()
);
}
}
#[test]
fn the_conversation_arrives_expanded_by_default() {
assert!(AutoExpand::default().responses);
assert!(!AutoExpand::default().others);
}
fn pair(auto: AutoExpand, pairs: &[(String, String)]) -> Vec<(Row, Row)> {
let payload = long();
let live = tx(vec![entry(EntryKind::Streaming {
thinking: "reasoning".into(),
text: payload.clone(),
})]);
let committed = tx(vec![entry(EntryKind::Model {
model_id: "opus".into(),
usage: Usage::default(),
blocks: vec![
Block::Thinking("reasoning".into()),
Block::Text(payload.clone()),
],
})]);
let folds = HashSet::new();
let speaker = super::rows::SPEAKER;
let (a, b) = (
rows(&live, speaker, auto, &folds),
rows(&committed, speaker, auto, &folds),
);
let of = |set: &[Row], prefix: &str| {
set.iter()
.find(|r| r.prefix == prefix)
.unwrap_or_else(|| panic!("no {prefix} row in {set:?}"))
.clone()
};
pairs
.iter()
.map(|(live, committed)| (of(&a, live), of(&b, committed)))
.collect()
}
fn answer_pair() -> (String, String) {
("live:".to_owned(), format!("{}:", super::rows::SPEAKER))
}
fn both() -> Vec<(String, String)> {
vec![
("thinking:".to_owned(), "thinking:".to_owned()),
answer_pair(),
]
}
#[test]
fn a_live_row_keeps_its_committed_counterparts_class_and_fold() {
let open = AutoExpand {
responses: true,
others: true,
};
for (live, committed) in pair(open, &both()) {
assert_eq!(live.class, committed.class, "{live:?} vs {committed:?}");
assert_eq!(
live.body.is_empty(),
committed.body.is_empty(),
"fold availability differs: {live:?} vs {committed:?}"
);
assert_eq!(live.preview, committed.preview, "the preview split differs");
assert!(
live.expanded && committed.expanded,
"both open on the knobs"
);
}
}
#[test]
fn a_live_row_is_open_even_when_the_knobs_are_shut() {
let shut = AutoExpand {
responses: false,
others: false,
};
for (live, committed) in pair(shut, &[answer_pair()]) {
assert!(live.expanded, "the live row is the show: {live:?}");
assert!(
!committed.expanded,
"the committed row folds: {committed:?}"
);
}
}
#[test]
fn the_live_tail_paints_as_its_committed_counterpart_does() {
let payload = long();
let open = AutoExpand {
responses: true,
others: true,
};
let live = tx(vec![entry(EntryKind::Streaming {
thinking: "reasoning".into(),
text: payload.clone(),
})]);
let committed = answer(&payload);
for (w, h) in SIZES {
let (l, c) = (
seen(&live, open, true, w, h),
seen(&committed, open, true, w, h),
);
let (lt, ct) = (run(&l, "abcdefghij"), run(&c, "abcdefghij"));
assert_eq!(lt.text, ct.text, "same glyphs on either side of the commit");
assert_eq!(lt.ink, ct.ink, "same ink on either side of the commit");
assert!(
whole(<) && whole(&ct),
"and whole on both sides at {w}x{h}: live {:.0}/{:.0}, committed {:.0}/{:.0}",
lt.shown.width(),
lt.laid.width(),
ct.shown.width(),
ct.laid.width()
);
}
}