use super::*;
use crate::output::Record;
fn line(seq: u64, text: &str) -> Line {
Line {
seq,
ts: "2026-08-22T14:30:12Z".to_string(),
record: Record::Stdout {
text: text.to_string(),
},
}
}
fn written(dir: &Path, lines: &[&str], finish: Option<Status>) -> PathBuf {
let writer = OpLogWriter::create(
dir,
uuid::Uuid::new_v4(),
"test_run",
"the declared suite",
&["wsb".to_string(), "test".to_string()],
chrono::Utc::now(),
)
.expect("the log opens");
for (seq, text) in lines.iter().enumerate() {
writer.write_line(&line(seq as u64, text));
}
if let Some(status) = finish {
writer.finish(status, None);
}
writer.path().to_path_buf()
}
#[test]
fn test8981_a_log_round_trips() {
let dir = tempfile::tempdir().unwrap();
let path = written(
dir.path(),
&["first", "second", "third"],
Some(Status::Completed),
);
let log = read(&path).expect("the log reads back");
assert_eq!(log.lines.len(), 3);
assert_eq!(log.header.label, "the declared suite");
assert_eq!(log.header.argv, vec!["wsb", "test"]);
assert_eq!(log.unreadable, 0);
let footer = log.footer.expect("a finished operation has a footer");
assert_eq!(footer.status, Status::Completed);
assert_eq!(footer.lines, 3);
match &log.lines[1].record {
Record::Stdout { text } => assert_eq!(text, "second"),
other => panic!("the record changed shape on the way to disk: {other:?}"),
}
assert_eq!(log.lines[1].ts, "2026-08-22T14:30:12Z");
}
#[test]
fn test8982_an_unfinished_log_has_no_footer() {
let dir = tempfile::tempdir().unwrap();
let path = written(dir.path(), &["started", "working"], None);
let log = read(&path).expect("an unfinished log still reads");
assert_eq!(log.lines.len(), 2);
assert!(
log.footer.is_none(),
"an operation that did not finish must not read as one that did"
);
let (header, footer) = read_envelope(&path).expect("the envelope reads");
assert_eq!(header.kind, "test_run");
assert!(footer.is_none());
}
#[test]
fn test8983_the_envelope_is_read_from_the_ends() {
let dir = tempfile::tempdir().unwrap();
let long: Vec<String> = (0..5000).map(|i| format!("line {i} {}", "x".repeat(200))).collect();
let refs: Vec<&str> = long.iter().map(String::as_str).collect();
let path = written(dir.path(), &refs, Some(Status::Failed));
let size = std::fs::metadata(&path).unwrap().len();
assert!(size > 1_000_000, "the fixture must be big enough to matter");
let (header, footer) = read_envelope(&path).expect("the envelope reads");
assert_eq!(header.label, "the declared suite");
let footer = footer.expect("a finished operation has a footer");
assert_eq!(footer.status, Status::Failed);
assert_eq!(footer.lines, 5000);
}
#[test]
fn test8984_unreadable_lines_are_counted() {
let dir = tempfile::tempdir().unwrap();
let path = written(dir.path(), &["good"], None);
{
use std::io::Write;
let mut file = std::fs::File::options().append(true).open(&path).unwrap();
writeln!(file, "{{\"seq\":1,\"type\":\"std").unwrap();
}
let log = read(&path).expect("a damaged log still reads");
assert_eq!(log.lines.len(), 1);
assert_eq!(
log.unreadable, 1,
"a line that could not be read must be counted, not hidden"
);
}
#[test]
fn test8985_recent_logs_are_newest_first() {
let dir = tempfile::tempdir().unwrap();
let ops = super::super::stream_dir(dir.path(), super::super::Stream::Ops);
let days: [(&str, &[&str]); 2] = [
("2026-08-21", &["090000", "170000"]),
("2026-08-22", &["080000"]),
];
for (day, times) in days {
for time in times {
let path = ops.join(day).join(format!("{time}-test-run-aaaaaaaa.jsonl"));
std::fs::create_dir_all(path.parent().unwrap()).unwrap();
std::fs::write(&path, "{}\n").unwrap();
}
}
let found = recent_logs(dir.path(), 10);
let names: Vec<String> = found
.iter()
.map(|p| {
format!(
"{}/{}",
p.parent().unwrap().file_name().unwrap().to_string_lossy(),
p.file_name().unwrap().to_string_lossy()
)
})
.collect();
assert_eq!(
names,
vec![
"2026-08-22/080000-test-run-aaaaaaaa.jsonl",
"2026-08-21/170000-test-run-aaaaaaaa.jsonl",
"2026-08-21/090000-test-run-aaaaaaaa.jsonl",
]
);
let two = recent_logs(dir.path(), 2);
assert_eq!(two.len(), 2);
assert!(two[0].ends_with("2026-08-22/080000-test-run-aaaaaaaa.jsonl"));
}