use tempfile::tempdir;
use super::{AGENT, step_dir, write_file, write_tool};
use crate::steps_view::{Doc, detail};
#[test]
fn detail_parses_records_splits_response_and_derives_tool_is_error() {
let dir = tempdir().unwrap();
let ws = dir.path();
write_file(ws, "001", "meta.json", br#"{"commit":"c"}"#);
write_file(ws, "001", "request.json", br#"{"model":"opus"}"#);
write_file(
ws,
"001",
"staging.json",
br#"[{"type":"text","text":"hi"}]"#,
);
write_file(
ws,
"001",
"response.json",
b"{\"type\":\"message_start\"}\nnot json\n{\"type\":\"end\"}\n",
);
write_tool(ws, "001", "toolu_1", b"{}", Some(br#"{"exit_code":0}"#));
write_tool(ws, "001", "toolu_2", b"{}", Some(br#"{"exit_code":2}"#));
write_tool(ws, "001", "toolu_0", b"{}", Some(b"oops"));
write_tool(ws, "001", "toolu_3", b"{}", Some(br#"{"stdout":"x"}"#));
std::fs::write(step_dir(ws, "001").join("tools").join(".keep"), b"").unwrap();
let d = detail(ws, AGENT, "001").unwrap();
assert!(matches!(d.meta, Doc::Json { .. }));
assert!(matches!(d.request, Doc::Json { .. }));
assert!(matches!(d.staging, Doc::Json { .. }));
assert_eq!(d.response.len(), 3);
assert!(matches!(d.response[0], Doc::Json { .. }));
assert_eq!(d.response[1], Doc::Unparsed(b"not json".to_vec()));
assert!(matches!(d.response[2], Doc::Json { .. }));
let ids: Vec<&str> = d.tools.iter().map(|t| t.tool_id.as_str()).collect();
assert_eq!(ids, vec!["toolu_0", "toolu_1", "toolu_2", "toolu_3"]);
let errs: Vec<bool> = d.tools.iter().map(|t| t.is_error).collect();
assert_eq!(errs, vec![false, false, true, false]);
assert_eq!(d.tools[0].output, Doc::Unparsed(b"oops".to_vec()));
}
#[test]
fn a_malformed_record_is_unparsed_not_absent_and_siblings_still_build() {
let dir = tempdir().unwrap();
let ws = dir.path();
write_file(ws, "001", "request.json", b"{ this is not json");
write_file(ws, "001", "meta.json", br#"{"commit":"c0ffee"}"#);
write_file(ws, "001", "staging.json", b" \n");
let d = detail(ws, AGENT, "001").unwrap();
assert_eq!(
d.request,
Doc::Unparsed(b"{ this is not json".to_vec()),
"malformed keeps its bytes AND says it is malformed"
);
assert_eq!(d.staging, Doc::Unparsed(b" \n".to_vec()));
assert!(
matches!(d.meta, Doc::Json { .. }),
"the sibling record parses"
);
assert!(d.response.is_empty(), "no response.json ⇒ no events");
}
#[test]
fn detail_is_forgiving_when_every_file_is_absent() {
let dir = tempdir().unwrap();
let ws = dir.path();
std::fs::create_dir_all(step_dir(ws, "001")).unwrap();
let d = detail(ws, AGENT, "001").unwrap();
assert_eq!(d.meta, Doc::Absent);
assert_eq!(d.request, Doc::Absent);
assert_eq!(d.staging, Doc::Absent);
assert!(d.response.is_empty());
assert!(d.tools.is_empty());
assert_eq!(d.seq, "001");
}
#[test]
fn a_seq_typed_as_a_bare_number_addresses_the_padded_record() {
let dir = tempdir().unwrap();
let ws = dir.path();
write_file(ws, "003", "meta.json", br#"{"commit":"c0ffee"}"#);
for typed in ["3", "03", "003", "0003"] {
let d = detail(ws, AGENT, typed).unwrap();
assert!(
matches!(d.meta, Doc::Json { .. }),
"{typed:?} must address the record"
);
assert_eq!(d.seq, "003", "and answer under the record's own name");
}
}
#[test]
fn a_seq_that_names_no_record_refuses_and_names_the_ones_that_do() {
let dir = tempdir().unwrap();
let ws = dir.path();
write_file(ws, "001", "meta.json", b"{}");
write_file(ws, "002", "meta.json", b"{}");
assert_eq!(
detail(ws, AGENT, "9").unwrap_err(),
"unknown step \"9\": steps here are 001, 002"
);
for typed in ["latest", ""] {
assert_eq!(
detail(ws, AGENT, typed).unwrap_err(),
format!("unknown step {typed:?}: steps here are 001, 002")
);
}
assert_eq!(
detail(ws, AGENT, "0").unwrap_err(),
"unknown step \"0\": steps here are 001, 002"
);
}
#[test]
fn a_conversation_with_no_steps_refuses_by_saying_it_has_none() {
let dir = tempdir().unwrap();
assert_eq!(
detail(dir.path(), AGENT, "1").unwrap_err(),
"unknown step \"1\": this conversation has taken no step"
);
}