use super::{AGENT, write_msg, write_response};
use crate::git_tree::Stream;
use crate::transcript::{EntryKind, Transcript, build};
use tempfile::tempdir;
#[test]
fn entries_are_ordered_by_filename_counter() {
let dir = tempdir().unwrap();
for n in ["003-c.md", "001-a.md", "002-b.md"] {
write_msg(dir.path(), n, b"x");
}
let t = build(dir.path(), AGENT);
let names: Vec<&str> = t.entries.iter().map(|e| e.name.as_str()).collect();
assert_eq!(names, ["001-a.md", "002-b.md", "003-c.md"]);
}
#[test]
fn non_file_entries_are_skipped() {
let dir = tempdir().unwrap();
write_msg(dir.path(), "001-a.md", b"x");
std::fs::create_dir_all(
dir.path()
.join("agents")
.join(AGENT)
.join("messages")
.join("subdir"),
)
.unwrap();
assert_eq!(build(dir.path(), AGENT).entries.len(), 1);
}
#[test]
fn absent_messages_dir_yields_empty_transcript() {
let dir = tempdir().unwrap();
assert!(build(dir.path(), AGENT).entries.is_empty());
}
#[test]
fn tool_in_progress_true_until_result_committed() {
let dir = tempdir().unwrap();
write_msg(
dir.path(),
"001-m.json",
br#"[{"type":"tool_use","id":"t1","name":"N","input":{}}]"#,
);
assert!(build(dir.path(), AGENT).tool_in_progress("t1"));
write_msg(
dir.path(),
"002-tool.json",
br#"{"tool_use_id":"t1","content":"ok"}"#,
);
assert!(!build(dir.path(), AGENT).tool_in_progress("t1"));
}
fn pulsing(t: &Transcript, id: &str) -> bool {
t.tool_in_progress(id)
}
#[test]
fn array_shaped_result_with_opaque_id_retires_the_pulse() {
let dir = tempdir().unwrap();
write_msg(
dir.path(),
"020-gpt-5.4.json",
br#"[{"type":"tool_use","id":"call_QxWh5oDZm5GNM4nbnIFVb7Ou","name":"bash","input":{"command":"pwd"}}]"#,
);
assert!(
pulsing(&build(dir.path(), AGENT), "call_QxWh5oDZm5GNM4nbnIFVb7Ou"),
"no result yet"
);
write_msg(
dir.path(),
"021-tool.json",
br#"[{"type":"tool_result","tool_use_id":"call_QxWh5oDZm5GNM4nbnIFVb7Ou","content":[{"type":"text","text":"/ops\n"}],"is_error":false}]"#,
);
let t = build(dir.path(), AGENT);
assert!(matches!(
&t.entries.last().unwrap().kind,
EntryKind::ToolResult { tool_use_id, content, is_error: false }
if tool_use_id == "call_QxWh5oDZm5GNM4nbnIFVb7Ou" && content == "/ops\n"
));
assert!(
!pulsing(&t, "call_QxWh5oDZm5GNM4nbnIFVb7Ou"),
"a committed result retires the in-flight query"
);
assert!(
t.entries.iter().any(|e| matches!(
&e.kind,
EntryKind::Model { blocks, .. }
if blocks.iter().any(|b| matches!(b, crate::transcript::Block::ToolUse { name, .. } if name == "bash"))
)),
"got: {:?}",
t.entries.iter().map(|e| e.name.clone()).collect::<Vec<_>>()
);
}
#[test]
fn build_reads_only_the_committed_messages() {
let dir = tempdir().unwrap();
write_msg(dir.path(), "001-a.md", b"hi");
write_response(
dir.path(),
1,
b"{\"type\":\"content_delta\",\"index\":0,\"delta\":{\"text_delta\":\"streaming\"}}\n",
);
assert_eq!(build(dir.path(), AGENT).entries.len(), 1);
}
#[test]
fn with_live_appends_the_stream_as_a_trailing_entry() {
let dir = tempdir().unwrap();
write_msg(dir.path(), "001-a.md", b"hi");
let t = build(dir.path(), AGENT).with_live(&Stream {
text: Some("streaming".into()),
thinking: Some("pondering".into()),
last_delta: Some(crate::git_tree::Delta::Text),
});
assert_eq!(t.entries.len(), 2);
assert!(matches!(
&t.entries[1].kind,
EntryKind::Streaming { thinking, text } if text == "streaming" && thinking == "pondering"
));
assert_eq!(t.entries[1].raw, b"ponderingstreaming");
}
#[test]
fn a_stream_that_has_said_nothing_appends_nothing() {
let dir = tempdir().unwrap();
write_msg(dir.path(), "001-a.md", b"hi");
let t = build(dir.path(), AGENT).with_live(&Stream::default());
assert_eq!(t.entries.len(), 1);
}
#[test]
fn a_second_live_fold_replaces_the_first_rather_than_standing_beside_it() {
let dir = tempdir().unwrap();
write_msg(dir.path(), "001-a.md", b"hi");
let committed = build(dir.path(), AGENT);
let said = |text: &str| Stream {
text: Some(text.into()),
thinking: None,
last_delta: Some(crate::git_tree::Delta::Text),
};
let once = committed.with_live(&said("the first half."));
let twice = committed
.with_live(&said("the first "))
.with_live(&said("the first half."));
assert_eq!(once, twice, "a.with_live(x).with_live(y) == a.with_live(y)");
assert_eq!(twice.entries.len(), 2, "the committed half plus ONE tail");
assert_eq!(
committed
.with_live(&said("said"))
.with_live(&Stream::default()),
committed
);
}