use super::super::derive::{depth, spend, wall_seconds};
use super::{repo, seg, usage_line, write_meta, write_response};
#[test]
fn spend_sums_every_segment_of_every_step() {
let r = repo();
let s1 = format!(
"{}{}",
seg(&usage_line(Some(5), Some(3), None, None)),
seg(&usage_line(Some(10), Some(2), None, None))
);
write_response(r.path(), "conv", 1, &s1);
write_response(
r.path(),
"conv",
2,
&seg(&usage_line(Some(4), Some(1), None, None)),
);
assert_eq!(spend(r.path(), "conv"), 25);
}
#[test]
fn spend_includes_descendant_subagents_not_unrelated_convs() {
let r = repo();
write_response(
r.path(),
"conv",
1,
&seg(&usage_line(Some(100), None, None, None)),
);
write_response(
r.path(),
"conv-child",
1,
&seg(&usage_line(Some(30), None, None, None)),
);
write_response(
r.path(),
"convX",
1,
&seg(&usage_line(Some(7), None, None, None)),
);
write_response(
r.path(),
"other",
1,
&seg(&usage_line(Some(999), None, None, None)),
);
assert_eq!(spend(r.path(), "conv"), 130);
}
#[test]
fn spend_none_counter_is_zero_and_cache_counters_count() {
let r = repo();
let body = seg(&usage_line(None, Some(7), Some(2), Some(1)));
write_response(r.path(), "conv", 1, &body);
assert_eq!(spend(r.path(), "conv"), 10);
}
#[test]
fn spend_zero_when_no_steps_dir() {
let r = repo();
assert_eq!(spend(r.path(), "conv"), 0);
}
#[test]
fn spend_tolerates_missing_response_nonnumeric_dir_and_malformed_lines() {
let r = repo();
std::fs::create_dir_all(r.path().join("steps/conv/001")).unwrap();
std::fs::create_dir_all(r.path().join("steps/conv/tools")).unwrap();
let body = format!(
"not json\n{}\n{{\"type\":\"end\"}}\n",
usage_line(Some(8), None, None, None)
);
write_response(r.path(), "conv", 2, &body);
assert_eq!(spend(r.path(), "conv"), 8);
}
#[test]
fn spend_tolerates_a_conv_entry_that_is_a_file() {
let r = repo();
write_response(
r.path(),
"conv",
1,
&seg(&usage_line(Some(6), None, None, None)),
);
std::fs::write(r.path().join("steps/conv-stray"), b"x").unwrap();
assert_eq!(spend(r.path(), "conv"), 6);
}
#[test]
fn wall_sums_step_spans_including_backoff_across_descent() {
let r = repo();
write_meta(
r.path(),
"conv",
1,
"2026-01-01T00:00:00Z",
"2026-01-01T00:00:10Z",
);
write_meta(
r.path(),
"conv",
2,
"2026-01-01T00:01:00Z",
"2026-01-01T00:01:05Z",
);
write_meta(
r.path(),
"conv-child",
1,
"2026-01-01T00:02:00Z",
"2026-01-01T00:02:03Z",
);
assert_eq!(wall_seconds(r.path(), "conv"), 18);
}
#[test]
fn wall_tolerates_missing_malformed_unparseable_and_backwards_spans() {
let r = repo();
std::fs::create_dir_all(r.path().join("steps/conv/001")).unwrap();
let d2 = r.path().join("steps/conv/002");
std::fs::create_dir_all(&d2).unwrap();
std::fs::write(d2.join("meta.json"), b"{ not json").unwrap();
write_meta(r.path(), "conv", 3, "iso-1", "iso-2");
write_meta(
r.path(),
"conv",
4,
"2026-01-01T00:00:10Z",
"2026-01-01T00:00:00Z",
);
write_meta(
r.path(),
"conv",
5,
"2026-01-01T00:00:00Z",
"2026-01-01T00:00:07Z",
);
assert_eq!(wall_seconds(r.path(), "conv"), 7);
}
#[test]
fn depth_counts_dispatch_levels_from_hyphenated_descent() {
assert_eq!(depth("20260422T065432Z-a1b2c3d4"), 0);
assert_eq!(
depth("20260422T065432Z-a1b2c3d4-20260422T065500Z-e5f6a7b8"),
1
);
assert_eq!(
depth("20260422T065432Z-a1b2c3d4-20260422T065500Z-e5f6a7b8-20260422T070000Z-deadbeef"),
2
);
}