#[cfg(test)]
mod smoke {
use crate::harness::message::Message;
use crate::harness::summarization::{
ConcatSummarizer, SummarizationPolicy, Summarizer, TrimStrategy, estimate_tokens,
trim_messages,
};
#[test]
fn estimate_tokens_basic() {
assert_eq!(estimate_tokens(""), 0);
assert!(estimate_tokens("hello world") > 0);
}
#[test]
fn trim_keep_last_preserves_system() {
let msgs = vec![
Message::system("sys"),
Message::user("first"),
Message::user("second"),
];
let trimmed = trim_messages(&msgs, &TrimStrategy::KeepLast(1));
assert_eq!(trimmed.len(), 2);
assert!(matches!(trimmed[0], Message::System(_)));
assert_eq!(trimmed[1].text(), "second");
}
#[test]
fn policy_should_not_summarize_short_messages() {
let policy = SummarizationPolicy {
trigger_tokens: 10_000,
keep_last: 4,
};
let msgs = vec![Message::user("hi"), Message::assistant("hello")];
assert!(!policy.should_summarize(&msgs));
}
#[tokio::test]
async fn concat_summarizer_produces_record() {
let summarizer = ConcatSummarizer;
let msgs = vec![Message::user("a"), Message::assistant("b")];
let record = summarizer.summarize(&msgs).await.expect("summarize failed");
assert!(!record.summary.text().is_empty());
assert_eq!(record.provenance.source_ids, vec!["msg-0", "msg-1"]);
assert!(record.provenance.original_token_estimate > 0);
}
#[test]
fn estimate_tokens_clamps_short_and_scales_long() {
assert_eq!(estimate_tokens(""), 0);
assert_eq!(estimate_tokens("x"), 1);
assert_eq!(estimate_tokens("abc"), 1);
assert_eq!(estimate_tokens("abcdefgh"), 2);
let long = "a".repeat(400);
assert_eq!(estimate_tokens(&long), 100);
}
#[test]
fn trim_keep_first_and_last() {
let msgs = vec![
Message::system("sys"),
Message::user("u1"),
Message::user("u2"),
Message::user("u3"),
Message::user("u4"),
];
let trimmed = trim_messages(&msgs, &TrimStrategy::KeepFirstAndLast { first: 1, last: 1 });
assert_eq!(trimmed.len(), 3);
assert!(matches!(trimmed[0], Message::System(_)));
assert_eq!(trimmed[1].text(), "u1");
assert_eq!(trimmed[2].text(), "u4");
}
#[test]
fn trim_keep_first_and_last_no_overlap_keeps_all() {
let msgs = vec![Message::user("u1"), Message::user("u2")];
let trimmed = trim_messages(&msgs, &TrimStrategy::KeepFirstAndLast { first: 2, last: 2 });
assert_eq!(trimmed.len(), 2);
}
#[test]
fn trim_keep_last_more_than_available() {
let msgs = vec![Message::user("only")];
let trimmed = trim_messages(&msgs, &TrimStrategy::KeepLast(5));
assert_eq!(trimmed.len(), 1);
assert_eq!(trimmed[0].text(), "only");
}
#[test]
fn trim_max_tokens_drops_oldest_non_system_first() {
let msgs = vec![
Message::system("ssss"),
Message::user("aaaaaaaa"),
Message::user("bbbbbbbb"),
Message::user("cccccccc"),
];
let trimmed = trim_messages(&msgs, &TrimStrategy::MaxTokens(5));
assert!(matches!(trimmed[0], Message::System(_)));
let texts: Vec<String> = trimmed.iter().map(|m| m.text()).collect();
assert!(texts.contains(&"ssss".to_string()));
assert!(texts.contains(&"cccccccc".to_string()));
assert!(!texts.contains(&"aaaaaaaa".to_string()));
}
#[test]
fn trim_max_tokens_drops_system_as_last_resort() {
let msgs = vec![
Message::system("a very long system instruction string here"),
Message::user("a very long user message string here too ok"),
];
let trimmed = trim_messages(&msgs, &TrimStrategy::MaxTokens(1));
assert!(trimmed.is_empty());
}
#[tokio::test]
async fn concat_summarizer_empty_is_error() {
let summarizer = ConcatSummarizer;
assert!(summarizer.summarize(&[]).await.is_err());
}
#[tokio::test]
async fn concat_summarizer_provenance_fields() {
let summarizer = ConcatSummarizer;
let msgs = vec![
Message::system("sys"),
Message::user("hello there"),
Message::assistant("general kenobi"),
];
let record = summarizer.summarize(&msgs).await.unwrap();
assert!(matches!(record.summary, Message::System(_)));
assert_eq!(
record.provenance.source_ids,
vec!["msg-0", "msg-1", "msg-2"]
);
assert!(record.provenance.reason.contains("ConcatSummarizer"));
assert!(record.provenance.original_token_estimate > 0);
assert!(record.provenance.summary_token_estimate > 0);
let text = record.summary.text();
assert!(text.contains("system:"));
assert!(text.contains("user:"));
assert!(text.contains("assistant:"));
}
#[test]
fn policy_should_summarize_over_trigger() {
let policy = SummarizationPolicy {
trigger_tokens: 2,
keep_last: 1,
};
let msgs = vec![Message::user("aaaaaaaaaaaaaaaa")];
assert!(policy.should_summarize(&msgs));
}
#[test]
fn policy_plan_splits_keeping_system_and_recent() {
let policy = SummarizationPolicy {
trigger_tokens: 0,
keep_last: 2,
};
let msgs = vec![
Message::system("sys"),
Message::user("old1"),
Message::user("old2"),
Message::user("recent1"),
Message::assistant("recent2"),
];
let (to_summarize, to_keep) = policy.plan(&msgs);
let sum_texts: Vec<String> = to_summarize.iter().map(|m| m.text()).collect();
assert_eq!(sum_texts, vec!["old1", "old2"]);
assert!(matches!(to_keep[0], Message::System(_)));
let keep_texts: Vec<String> = to_keep.iter().map(|m| m.text()).collect();
assert_eq!(keep_texts, vec!["sys", "recent1", "recent2"]);
}
#[test]
fn policy_plan_keeps_everything_when_few_messages() {
let policy = SummarizationPolicy {
trigger_tokens: 0,
keep_last: 5,
};
let msgs = vec![Message::system("sys"), Message::user("u")];
let (to_summarize, to_keep) = policy.plan(&msgs);
assert!(to_summarize.is_empty());
assert_eq!(to_keep.len(), 2);
}
}