use super::*;
use crate::core::sm::context::mock_provider::MockProvider;
use crate::core::sm::context::model::{Round, ToolTrace};
use chrono::{TimeZone, Utc};
fn ts() -> chrono::DateTime<Utc> {
Utc.with_ymd_and_hms(2026, 1, 1, 0, 0, 0)
.single()
.expect("valid ts")
}
#[test]
fn estimate_tokens_uses_chars_over_four() {
assert_eq!(estimate_tokens(0), 0);
assert_eq!(estimate_tokens(4), 1);
assert_eq!(estimate_tokens(7), 1);
assert_eq!(estimate_tokens(8), 2);
assert_eq!(estimate_tokens(40_000), 10_000);
}
#[test]
fn faithful_prompt_mentions_required_anchors() {
let p = FAITHFUL_SUMMARY_SYSTEM_PROMPT;
assert!(p.contains("goal ids"), "must preserve goal ids");
assert!(p.contains("session ids"), "must preserve session ids");
assert!(p.contains("decisions"), "must preserve decisions");
assert!(p.contains("blockers"), "must preserve blockers");
assert!(p.contains("open questions"), "must preserve open questions");
}
#[test]
fn resummarise_prompt_mentions_required_anchors() {
let p = COMPRESS_SUMMARY_SYSTEM_PROMPT;
assert!(p.contains("goal id"));
assert!(p.contains("session id"));
assert!(p.contains("decision"));
assert!(p.contains("blocker"));
assert!(p.contains("open question"));
}
#[test]
fn render_fold_message_includes_summary_and_rounds() {
let rounds = vec![Round::new(
"deploy goal g-42",
"spawned s-7",
ts(),
vec![ToolTrace::new("session_new", "s-7 created")],
)];
let msg = render_compaction_user_message("prior summary text", &rounds);
assert!(msg.contains("prior summary text"));
assert!(msg.contains("deploy goal g-42"));
assert!(msg.contains("spawned s-7"));
assert!(msg.contains("session_new"));
assert!(msg.contains("s-7 created"));
}
#[test]
fn render_fold_message_marks_empty_prior_summary() {
let rounds = vec![Round::new("u", "a", ts(), Vec::new())];
let msg = render_compaction_user_message("", &rounds);
assert!(msg.contains("none yet"));
}
#[test]
fn render_resummarise_message_wraps_summary() {
let msg = render_resummarise_user_message("big summary g-1 s-2");
assert!(msg.contains("SUMMARY TO COMPACT"));
assert!(msg.contains("big summary g-1 s-2"));
}
#[tokio::test]
async fn fold_rounds_builds_haiku_request_at_temp_zero() {
let mock = MockProvider::fixed("updated summary");
let rounds = vec![Round::new("u", "a", ts(), Vec::new())];
let resp = fold_rounds(&mock, "claude-haiku", "", &rounds)
.await
.expect("fold succeeds");
assert_eq!(resp.text, "updated summary");
let reqs = mock.requests();
assert_eq!(reqs.len(), 1, "exactly one compaction call");
assert_eq!(reqs[0].model, "claude-haiku");
assert!((reqs[0].temperature - COMPACTION_TEMPERATURE).abs() < f32::EPSILON);
assert_eq!(reqs[0].max_tokens, COMPACTION_MAX_TOKENS);
assert_eq!(reqs[0].system, FAITHFUL_SUMMARY_SYSTEM_PROMPT);
}
#[tokio::test]
async fn fold_rounds_returns_mock_summary() {
let mock = MockProvider::fixed("CANNED-123");
let rounds = vec![Round::new("hello", "world", ts(), Vec::new())];
let resp = fold_rounds(&mock, "m", "prior", &rounds)
.await
.expect("fold");
assert_eq!(resp.text, "CANNED-123");
}
#[tokio::test]
async fn resummarise_returns_mock_summary() {
let mock = MockProvider::fixed("shorter");
let resp = resummarise(&mock, "m", "long summary")
.await
.expect("resummarise");
assert_eq!(resp.text, "shorter");
let reqs = mock.requests();
assert_eq!(reqs[0].system, COMPRESS_SUMMARY_SYSTEM_PROMPT);
assert!((reqs[0].temperature - COMPACTION_TEMPERATURE).abs() < f32::EPSILON);
}