use super::*;
use trusty_mpm::core::savings::{
SavingsRow, TECHNIQUE_COMPRESS, TECHNIQUE_INSTRUCTION_COMPRESSION, append_row, now_ts,
};
use trusty_mpm::core::session_record::{KIND_MODEL, KIND_TRANSCRIPT, read_session_record};
fn total(tokens_saved: u64, tokens_before: u64) -> SavingsTotal {
SavingsTotal {
tokens_saved,
tokens_before,
cost_saved_usd: 0.01,
rows: 1,
}
}
fn write_row(ledger: &Path, session_id: &str, tokens_saved: i64, tokens_before: u64) {
write_technique_row(
ledger,
session_id,
TECHNIQUE_COMPRESS,
tokens_saved,
tokens_before,
);
}
fn write_technique_row(
ledger: &Path,
session_id: &str,
technique: &str,
tokens_saved: i64,
tokens_before: u64,
) {
append_row(
ledger,
&SavingsRow {
ts: now_ts(),
session_id: session_id.to_string(),
technique: technique.to_string(),
tokens_saved,
tokens_before,
cost_saved_usd: 0.18,
basis: "fixture".to_string(),
model_source: "launch-config".to_string(),
},
)
.expect("append");
}
#[test]
fn an_instruction_compression_row_alone_renders_the_empty_state() {
let dir = tempfile::tempdir().expect("temp dir");
let ledger = dir.path().join("savings.jsonl");
write_technique_row(
&ledger,
"sess-1",
TECHNIQUE_INSTRUCTION_COMPRESSION,
12_000,
60_000,
);
assert_eq!(
savings_segment_at(&ledger, "sess-1").as_deref(),
Some(EMPTY_STATE),
"an instruction-fold row is not a tool-output compression measurement"
);
}
#[test]
fn one_compress_row_moves_the_segment() {
let dir = tempfile::tempdir().expect("temp dir");
let ledger = dir.path().join("savings.jsonl");
write_technique_row(
&ledger,
"sess-1",
TECHNIQUE_INSTRUCTION_COMPRESSION,
12_000,
60_000,
);
assert_eq!(
savings_segment_at(&ledger, "sess-1").as_deref(),
Some(EMPTY_STATE)
);
write_row(&ledger, "sess-1", 12_000, 60_000);
assert_eq!(
savings_segment_at(&ledger, "sess-1").as_deref(),
Some("\u{1f4b8}20%/20%"),
"the compress row must be visible on the next render"
);
}
#[test]
fn savings_segment_renders_a_percent() {
assert_eq!(
render_savings_segment(&total(1, 3), &[33]).as_deref(),
Some("\u{1f4b8}33%/33%")
);
assert_eq!(
render_savings_segment(&total(5_000, 20_000), &[25]).as_deref(),
Some("\u{1f4b8}25%/25%")
);
}
#[test]
fn savings_segment_renders_the_average_beside_the_latest_row() {
assert_eq!(
render_savings_segment(&total(5_000, 20_000), &[25, 29, 33]).as_deref(),
Some("\u{1f4b8}33%/29%"),
"the newest row is the left figure; the mean of all three is the right"
);
}
#[test]
fn render_uses_the_latest_row_not_the_session_total() {
assert_eq!(
render_savings_segment(&total(15_000, 70_000), &[20, 30]).as_deref(),
Some("\u{1f4b8}30%/25%"),
"21% — the session total — must not appear on either side"
);
}
#[test]
fn savings_segment_renders_the_latest_row_not_the_session_total() {
let dir = tempfile::tempdir().expect("temp dir");
let ledger = savings_log_in(dir.path());
write_row(&ledger, "sess-1", 12_000, 60_000); write_row(&ledger, "sess-1", 3_000, 10_000);
assert_eq!(
savings_segment_at(&ledger, "sess-1").as_deref(),
Some("\u{1f4b8}30%/25%"),
"the newest row's 30% must be the left figure, not the 21% session total"
);
}
#[test]
fn savings_segment_is_absent_on_a_zero_fold() {
assert_eq!(render_savings_segment(&SavingsTotal::default(), &[]), None);
assert_eq!(
render_savings_segment(
&SavingsTotal {
tokens_saved: 0,
tokens_before: 0,
cost_saved_usd: 0.0,
rows: 3,
},
&[40],
),
None,
"a zero fold renders nothing, whatever percents are in hand"
);
}
#[test]
fn savings_segment_is_absent_without_a_percent_denominator() {
assert_eq!(
render_savings_segment(
&SavingsTotal {
tokens_saved: 4_000,
tokens_before: 0,
cost_saved_usd: 0.01,
rows: 1,
},
&[],
),
None
);
}
#[test]
fn savings_segment_never_renders_zero_percent() {
for (tokens_saved, tokens_before) in [(1_i64, 200_u64), (12_000, 12_000_100), (5, 1_000)] {
let dir = tempfile::tempdir().expect("temp dir");
let ledger = savings_log_in(dir.path());
write_row(&ledger, "sess-1", tokens_saved, tokens_before);
assert_eq!(
savings_segment_at(&ledger, "sess-1").as_deref(),
Some("\u{1f4b8}1%/1%"),
"a tiny but real saving rounds up to 1%, never down to 0% \
(tokens_saved={tokens_saved}, tokens_before={tokens_before})"
);
}
}
#[test]
fn savings_segment_is_absent_when_the_ledger_is_missing() {
let dir = tempfile::tempdir().expect("temp dir");
let ledger = dir.path().join("usage").join("savings.jsonl");
assert_eq!(
savings_segment_at(&ledger, "sess-1").as_deref(),
Some(EMPTY_STATE)
);
}
#[test]
fn savings_segment_is_absent_on_an_empty_ledger() {
let dir = tempfile::tempdir().expect("temp dir");
let ledger = savings_log_in(dir.path());
std::fs::create_dir_all(ledger.parent().expect("parent")).expect("mkdir");
std::fs::write(&ledger, "").expect("write empty ledger");
assert_eq!(
savings_segment_at(&ledger, "sess-1").as_deref(),
Some(EMPTY_STATE)
);
}
#[test]
fn savings_segment_reads_the_ledger_under_an_explicit_root() {
let dir = tempfile::tempdir().expect("temp dir");
let ledger = savings_log_in(dir.path());
write_row(&ledger, "sess-1", 12_000, 60_000);
assert_eq!(
savings_segment_at(&ledger, "sess-1").as_deref(),
Some("\u{1f4b8}20%/20%")
);
assert_eq!(
savings_segment_at(&ledger, "sess-2").as_deref(),
Some(EMPTY_STATE)
);
}
#[test]
fn savings_segment_folds_a_sibling_session_id() {
let dir = tempfile::tempdir().expect("temp dir");
let ledger = savings_log_in(dir.path());
write_row(&ledger, "claude-old", 12_000, 60_000);
trusty_mpm::core::session_links::record_link(dir.path(), "managed-1", "claude-old");
trusty_mpm::core::session_links::record_link(dir.path(), "managed-1", "claude-new");
assert_eq!(
savings_segment_at_in(dir.path(), &ledger, "claude-new").as_deref(),
Some("\u{1f4b8}20%/20%"),
"a restart's new id must fold its managed session's earlier rows"
);
}
#[test]
fn savings_segment_prefers_this_sessions_own_rows() {
let dir = tempfile::tempdir().expect("temp dir");
let ledger = savings_log_in(dir.path());
write_row(&ledger, "claude-old", 12_000, 60_000);
write_row(&ledger, "claude-new", 30_000, 60_000);
trusty_mpm::core::session_links::record_link(dir.path(), "managed-1", "claude-old");
trusty_mpm::core::session_links::record_link(dir.path(), "managed-1", "claude-new");
assert_eq!(
savings_segment_at_in(dir.path(), &ledger, "claude-new").as_deref(),
Some("\u{1f4b8}50%/50%"),
"this session's own 50% must win over the sibling's 20%, on both sides"
);
}
#[test]
fn savings_segment_renders_the_empty_state_on_a_zero_fold() {
let dir = tempfile::tempdir().expect("temp dir");
let ledger = savings_log_in(dir.path());
write_row(&ledger, "claude-old", 12_000, 60_000);
for id in ["never-seen", ""] {
assert_eq!(
savings_segment_at_in(dir.path(), &ledger, id).as_deref(),
Some(EMPTY_STATE),
"session id {id:?} must render the explicit empty state"
);
}
assert_ne!(
EMPTY_STATE, "\u{1f4b8}0%",
"the empty state must never be mistakable for a measured zero"
);
}
#[test]
fn savings_segment_averages_this_sessions_own_rows() {
let dir = tempfile::tempdir().expect("temp dir");
let ledger = savings_log_in(dir.path());
write_row(&ledger, "sess-a", 100, 1_000); write_row(&ledger, "sess-b", 900, 1_000); write_row(&ledger, "sess-a", 3_000, 10_000);
assert_eq!(
savings_segment_at(&ledger, "sess-a").as_deref(),
Some("\u{1f4b8}30%/20%"),
"sess-b's 90% must reach neither figure"
);
}
#[test]
fn rendering_writes_nothing_under_the_usage_directory() {
let dir = tempfile::tempdir().expect("temp dir");
let ledger = savings_log_in(dir.path());
write_row(&ledger, "sess-a", 100, 1_000);
write_row(&ledger, "sess-b", 300, 1_000);
let usage_dir = ledger.parent().expect("usage dir").to_path_buf();
let listing = |dir: &Path| -> Vec<(String, u64)> {
let mut entries: Vec<(String, u64)> = std::fs::read_dir(dir)
.expect("read usage dir")
.map(|entry| {
let entry = entry.expect("entry");
let len = entry.metadata().expect("metadata").len();
(entry.file_name().to_string_lossy().into_owned(), len)
})
.collect();
entries.sort();
entries
};
let before = listing(&usage_dir);
assert!(
savings_segment_at(&ledger, "sess-a").is_some(),
"the fixture must render, or this test proves nothing"
);
assert_eq!(
listing(&usage_dir),
before,
"rendering must add or grow no file under usage/"
);
}
#[test]
fn savings_segment_probe_is_absent_without_a_session_id() {
assert_eq!(savings_segment_probe("").as_deref(), Some(EMPTY_STATE));
}
fn transcript_under(config_dir: &Path, session_id: &str) -> PathBuf {
let projects = config_dir.join("projects").join("slug");
std::fs::create_dir_all(&projects).expect("mkdir");
let transcript = projects.join(format!("{session_id}.jsonl"));
std::fs::write(&transcript, "{}\n").expect("write transcript");
transcript
}
fn recorded_transcript(root: &Path, session_id: &str) -> Option<String> {
read_session_record(root, KIND_TRANSCRIPT, session_id)
}
#[test]
fn a_transcript_path_outside_the_config_dir_is_not_recorded() {
let root = tempfile::tempdir().expect("temp dir");
let config = tempfile::tempdir().expect("temp dir");
let elsewhere = tempfile::tempdir().expect("temp dir");
let outside = elsewhere.path().join("stolen.jsonl");
std::fs::write(&outside, "{}\n").expect("write");
record_session_facts_at(
root.path(),
Some(config.path()),
"sess-1",
"claude-opus-4-1",
"/etc/passwd",
"",
);
assert_eq!(recorded_transcript(root.path(), "sess-1"), None);
record_session_facts_at(
root.path(),
Some(config.path()),
"sess-1",
"claude-opus-4-1",
&outside.to_string_lossy(),
"",
);
assert_eq!(recorded_transcript(root.path(), "sess-1"), None);
assert_eq!(
read_session_record(root.path(), KIND_MODEL, "sess-1").as_deref(),
Some("claude-opus-4-1"),
"a rejected transcript path must not cost the model record"
);
}
#[test]
fn a_traversing_transcript_path_is_not_recorded() {
let root = tempfile::tempdir().expect("temp dir");
let config = tempfile::tempdir().expect("temp dir");
transcript_under(config.path(), "sess-1");
let traversing = config
.path()
.join("projects")
.join("..")
.join("..")
.join("etc")
.join("passwd");
record_session_facts_at(
root.path(),
Some(config.path()),
"sess-1",
"claude-opus-4-1",
&traversing.to_string_lossy(),
"",
);
assert_eq!(recorded_transcript(root.path(), "sess-1"), None);
}
#[test]
fn a_transcript_path_is_not_recorded_without_a_config_dir() {
let root = tempfile::tempdir().expect("temp dir");
let config = tempfile::tempdir().expect("temp dir");
let transcript = transcript_under(config.path(), "sess-1");
record_session_facts_at(
root.path(),
None,
"sess-1",
"claude-opus-4-1",
&transcript.to_string_lossy(),
"",
);
assert_eq!(
recorded_transcript(root.path(), "sess-1"),
None,
"with no config directory there is nothing to contain the path"
);
assert_eq!(
read_session_record(root.path(), KIND_MODEL, "sess-1").as_deref(),
Some("claude-opus-4-1"),
"the model carries no path, so it is still recorded"
);
}
#[test]
fn a_transcript_path_under_the_config_dir_is_recorded() {
let root = tempfile::tempdir().expect("temp dir");
let config = tempfile::tempdir().expect("temp dir");
let transcript = transcript_under(config.path(), "sess-1");
record_session_facts_at(
root.path(),
Some(config.path()),
"sess-1",
"claude-opus-4-1",
&transcript.to_string_lossy(),
"",
);
assert_eq!(
recorded_transcript(root.path(), "sess-1"),
Some(
transcript
.canonicalize()
.expect("canonicalize")
.to_string_lossy()
.into_owned()
)
);
}
#[test]
fn the_first_render_records_the_startup_context() {
let root = tempfile::tempdir().expect("temp dir");
let config = tempfile::tempdir().expect("temp dir");
let project = tempfile::tempdir().expect("temp dir");
let transcript = transcript_under(config.path(), "sess-1");
std::fs::write(
&transcript,
"{\"type\":\"assistant\",\"message\":{\"id\":\"m1\",\"usage\":{\"input_tokens\":4,\
\"cache_creation_input_tokens\":74685,\"cache_read_input_tokens\":27297,\
\"output_tokens\":9}}}\n",
)
.expect("write transcript");
record_session_facts_at(
root.path(),
Some(config.path()),
"sess-1",
"claude-opus-4-1",
&transcript.to_string_lossy(),
&project.path().to_string_lossy(),
);
let stored = trusty_mpm::core::startup_context::read_startup_context(root.path(), "sess-1")
.expect("a startup reading");
assert_eq!(stored.tokens, 101_986);
record_session_facts_at(
root.path(),
Some(config.path()),
"sess-1",
"claude-opus-4-1",
&transcript.to_string_lossy(),
"",
);
assert_eq!(
trusty_mpm::core::startup_context::read_startup_context(root.path(), "sess-1")
.expect("still there")
.tokens,
101_986
);
}