use super::*;
#[test]
fn test_micro_compact_basic() {
let mut messages = vec![Message::system("System prompt")];
for i in 0..15 {
messages.push(Message::user(format!("Question {}", i)));
messages.push(Message::assistant(format!("Answer {}", i)));
}
messages.push(Message::user("Current question"));
let metrics = micro_compact(&mut messages);
assert_eq!(metrics.method, CompressionMethod::Micro);
assert!(
messages.len() <= 26,
"Expected at most 26 messages, got {}",
messages.len()
);
assert_eq!(messages[0].role, "system"); }
#[test]
fn test_micro_compact_keeps_recent() {
let mut messages = vec![Message::system("System prompt")];
for i in 0..10 {
messages.push(Message::user(format!("Old user {}", i)));
messages.push(Message::assistant(format!("Old assistant {}", i)));
}
messages.push(Message::user("Recent user"));
messages.push(Message::assistant("Recent assistant"));
messages.push(Message::user("Final question"));
micro_compact(&mut messages);
let has_recent_user = messages
.iter()
.any(|m| m.content.text().contains("Recent user"));
assert!(has_recent_user, "Recent user message should be preserved");
}
#[test]
fn test_micro_compact_strips_reasoning() {
let mut messages = vec![Message::system("System prompt")];
for i in 0..20 {
messages.push(Message::user(format!("Old question {}", i)));
messages.push(Message::assistant_with_reasoning(
format!("Old answer {}", i),
format!("Old reasoning {}", i),
));
}
messages.push(Message::user("Recent question"));
messages.push(Message::assistant_with_reasoning(
"Recent answer",
"Recent reasoning",
));
micro_compact(&mut messages);
let has_old_answer = messages
.iter()
.any(|m| m.content.text().contains("Old answer 0"));
let has_recent_answer = messages
.iter()
.any(|m| m.content.text().contains("Recent answer"));
assert!(!has_old_answer, "Old messages should be compressed");
assert!(has_recent_answer, "Recent messages should be kept");
}
#[test]
fn test_file_access_tracker() {
let mut tracker = FileAccessTracker::new(5);
tracker.record_access("file1.rs");
tracker.record_access("file2.rs");
tracker.record_access("file3.rs");
let recent = tracker.get_recent_files(2);
assert_eq!(recent.len(), 2);
assert_eq!(recent[0], "file3.rs"); assert_eq!(recent[1], "file2.rs");
}
#[test]
fn test_file_access_tracker_updates_timestamp() {
let mut tracker = FileAccessTracker::new(5);
tracker.record_access("file1.rs");
std::thread::sleep(std::time::Duration::from_millis(10));
tracker.record_access("file2.rs");
std::thread::sleep(std::time::Duration::from_millis(10));
tracker.record_access("file1.rs");
let recent = tracker.get_recent_files(2);
assert_eq!(recent[0], "file1.rs"); }
#[test]
fn test_compression_metrics() {
let metrics = CompressionMetrics::new(CompressionMethod::Auto, 1000, 600, 20, 8, 150);
assert_eq!(metrics.tokens_saved, 400);
assert!(metrics.summary().contains("AutoCompact"));
assert!(metrics.summary().contains("400"));
}
#[test]
fn test_auto_compact_config_default() {
let config = AutoCompactConfig::default();
assert_eq!(config.token_threshold, 80);
assert_eq!(config.reserve_buffer, 13_000);
assert_eq!(config.max_summary_tokens, 20_000);
assert_eq!(config.max_consecutive_failures, 3);
}
#[test]
fn test_auto_compact_manager_circuit_breaker() {
let mut manager = AutoCompactManager::new(AutoCompactConfig::default());
for _ in 0..3 {
manager.record_failure();
}
assert!(manager.is_circuit_open());
manager.reset_circuit();
assert!(!manager.is_circuit_open());
assert_eq!(manager.consecutive_failures, 0);
}
#[test]
fn test_compression_method_display() {
assert_eq!(CompressionMethod::Micro.to_string(), "MicroCompact");
assert_eq!(CompressionMethod::Auto.to_string(), "AutoCompact");
assert_eq!(CompressionMethod::Full.to_string(), "FullCompact");
}
#[test]
fn test_micro_compact_small_conversation() {
let mut messages = vec![
Message::system("System prompt"),
Message::user("Question 1"),
Message::assistant("Answer 1"),
Message::user("Question 2"),
];
let metrics = micro_compact(&mut messages);
assert_eq!(metrics.messages_before, metrics.messages_after);
}
#[test]
fn test_orchestrator_total_tokens_saved() {
let mut orchestrator = CompressionOrchestrator::new();
orchestrator.metrics_history.push(CompressionMetrics::new(
CompressionMethod::Micro,
1000,
800,
20,
15,
50,
));
orchestrator.metrics_history.push(CompressionMetrics::new(
CompressionMethod::Auto,
800,
500,
15,
8,
100,
));
assert_eq!(orchestrator.total_tokens_saved(), 500);
}
#[test]
fn test_micro_compact_preserves_tool_calls() {
use crate::api::types::{ToolCall, ToolFunction};
let mut messages = vec![
Message::system("System prompt"),
Message::user("Old question"),
Message {
role: "assistant".to_string(),
content: MessageContent::Text("Let me check".to_string()),
reasoning_content: None,
tool_calls: Some(vec![ToolCall {
id: "call_1".to_string(),
call_type: "function".to_string(),
function: ToolFunction {
name: "file_read".to_string(),
arguments: r#"{"path": "test.rs"}"#.to_string(),
},
}]),
tool_call_id: None,
name: None,
},
Message::user("Recent question"),
Message::assistant("Recent answer"),
];
for i in 0..15 {
messages.push(Message::user(format!("Question {}", i)));
messages.push(Message::assistant(format!("Answer {}", i)));
}
let metrics = micro_compact(&mut messages);
assert!(metrics.messages_after < metrics.messages_before);
assert_eq!(messages[0].role, "system");
}
#[test]
fn test_file_access_tracker_get_tracked_files() {
let mut tracker = FileAccessTracker::new(5);
let tracked = tracker.get_tracked_files();
assert!(tracked.is_empty());
tracker.record_access("file1.rs");
std::thread::sleep(std::time::Duration::from_millis(5));
tracker.record_access("file2.rs");
let tracked = tracker.get_tracked_files();
assert_eq!(tracked.len(), 2);
for (_, duration) in &tracked {
assert!(duration.as_secs() < 1); }
}
#[test]
fn test_file_access_tracker_is_empty_and_len() {
let mut tracker = FileAccessTracker::new(5);
assert!(tracker.is_empty());
assert_eq!(tracker.len(), 0);
tracker.record_access("file1.rs");
assert!(!tracker.is_empty());
assert_eq!(tracker.len(), 1);
tracker.record_access("file2.rs");
assert_eq!(tracker.len(), 2);
}
#[test]
fn test_file_access_tracker_clear() {
let mut tracker = FileAccessTracker::new(5);
tracker.record_access("file1.rs");
tracker.record_access("file2.rs");
assert_eq!(tracker.len(), 2);
tracker.clear();
assert!(tracker.is_empty());
assert_eq!(tracker.len(), 0);
assert!(tracker.get_recent_files(10).is_empty());
}
#[test]
fn test_file_access_tracker_capacity_limit() {
let mut tracker = FileAccessTracker::new(3);
tracker.record_access("file1.rs");
tracker.record_access("file2.rs");
tracker.record_access("file3.rs");
tracker.record_access("file4.rs");
assert_eq!(tracker.len(), 3);
let recent = tracker.get_recent_files(3);
assert_eq!(recent.len(), 3);
assert_eq!(recent[0], "file4.rs"); assert_eq!(recent[1], "file3.rs");
assert_eq!(recent[2], "file2.rs"); }
#[test]
fn test_auto_compact_manager_with_threshold() {
let manager = AutoCompactManager::with_threshold(70);
assert_eq!(manager.config.token_threshold, 70);
assert_eq!(manager.config.reserve_buffer, 13_000); assert!(!manager.is_circuit_open());
}
#[test]
fn test_auto_compact_manager_should_compress() {
let manager = AutoCompactManager::with_threshold(80);
assert!(!manager.should_compress(70_000, 100_000));
assert!(manager.should_compress(80_000, 100_000));
assert!(manager.should_compress(90_000, 100_000));
assert!(manager.should_compress(800, 1000));
assert!(!manager.should_compress(799, 1000));
}
#[test]
fn test_auto_compact_manager_should_compress_circuit_breaker() {
let mut manager = AutoCompactManager::with_threshold(80);
assert!(manager.should_compress(90_000, 100_000));
for _ in 0..3 {
manager.record_failure();
}
assert!(manager.is_circuit_open());
assert!(!manager.should_compress(90_000, 100_000));
}
#[test]
fn test_auto_compact_manager_record_success() {
let mut manager = AutoCompactManager::new(AutoCompactConfig::default());
manager.record_failure();
manager.record_failure();
assert_eq!(manager.consecutive_failures, 2);
let metrics = CompressionMetrics::new(CompressionMethod::Auto, 1000, 600, 20, 8, 150);
manager.record_success(metrics.clone());
assert_eq!(manager.consecutive_failures, 0);
assert!(manager.last_compression().is_some());
assert_eq!(manager.last_compression().unwrap().tokens_saved, 400);
}
#[test]
fn test_compression_orchestrator_with_config() {
let config = AutoCompactConfig {
token_threshold: 75,
reserve_buffer: 10_000,
max_summary_tokens: 15_000,
max_consecutive_failures: 5,
};
let orchestrator = CompressionOrchestrator::with_config(config.clone());
assert!(orchestrator.metrics_history().is_empty());
assert!(orchestrator.file_tracker().is_empty());
assert_eq!(orchestrator.total_tokens_saved(), 0);
}
#[test]
fn test_compression_orchestrator_record_file_access() {
let mut orchestrator = CompressionOrchestrator::new();
orchestrator.record_file_access("src/main.rs");
orchestrator.record_file_access("src/lib.rs");
let tracker = orchestrator.file_tracker();
assert_eq!(tracker.len(), 2);
let recent = tracker.get_recent_files(2);
assert!(recent.contains(&"src/main.rs".to_string()));
assert!(recent.contains(&"src/lib.rs".to_string()));
}
#[test]
fn test_compression_orchestrator_reset() {
let mut orchestrator = CompressionOrchestrator::new();
orchestrator.record_file_access("file.rs");
orchestrator.metrics_history.push(CompressionMetrics::new(
CompressionMethod::Micro,
1000,
800,
20,
15,
50,
));
orchestrator.auto_manager.record_failure();
orchestrator.auto_manager.record_failure();
orchestrator.auto_manager.record_failure();
assert!(orchestrator.auto_manager.is_circuit_open());
orchestrator.reset();
assert!(orchestrator.file_tracker().is_empty());
assert!(orchestrator.metrics_history().is_empty());
assert!(!orchestrator.auto_manager.is_circuit_open());
assert_eq!(orchestrator.total_tokens_saved(), 0);
}
#[test]
fn test_micro_compact_exactly_at_threshold() {
let mut messages = vec![Message::system("System prompt")];
for i in 0..5 {
messages.push(Message::user(format!("Question {}", i)));
messages.push(Message::assistant(format!("Answer {}", i)));
}
messages.push(Message::user("Final"));
let metrics = micro_compact(&mut messages);
assert_eq!(metrics.messages_before, metrics.messages_after);
}
#[test]
fn test_micro_compact_at_13_messages() {
let mut messages = vec![Message::system("System prompt")];
for i in 0..5 {
messages.push(Message::user(format!("Question {}", i)));
messages.push(Message::assistant(format!("Answer {}", i)));
}
messages.push(Message::user("Final 1"));
messages.push(Message::user("Final 2"));
assert_eq!(messages.len(), 13);
let metrics = micro_compact(&mut messages);
assert_eq!(metrics.messages_before, metrics.messages_after);
}
#[test]
fn test_micro_compact_with_tool_messages() {
let mut messages = vec![Message::system("System prompt")];
for i in 0..8 {
messages.push(Message::user(format!("Request {}", i)));
messages.push(Message {
role: "tool".to_string(),
content: MessageContent::Text(format!("Tool result {} with lots of content here", i)),
reasoning_content: None,
tool_calls: None,
tool_call_id: Some(format!("call_{}", i)),
name: Some("test_tool".to_string()),
});
}
for i in 0..8 {
messages.push(Message::user(format!("Recent {}", i)));
messages.push(Message::assistant(format!("Answer {}", i)));
}
let metrics = micro_compact(&mut messages);
assert!(metrics.messages_after < metrics.messages_before);
let summary_msg = &messages[1];
assert!(summary_msg.content.text().contains("tool"));
}
#[test]
fn test_micro_compact_empty_messages() {
let mut messages: Vec<Message> = vec![];
let metrics = micro_compact(&mut messages);
assert_eq!(metrics.messages_before, 0);
assert_eq!(metrics.messages_after, 0);
assert!(messages.is_empty());
}
#[test]
fn test_micro_compact_reasoning_stripping() {
let mut messages = vec![Message::system("System prompt")];
for i in 0..15 {
messages.push(Message::user(format!("Question {}", i)));
messages.push(Message::assistant_with_reasoning(
format!("Answer {}", i),
format!("Reasoning for answer {} with detailed thought process", i),
));
}
let _metrics = micro_compact(&mut messages);
let reasoning_count = messages
.iter()
.filter(|m| m.reasoning_content.is_some())
.count();
assert!(
reasoning_count <= 4,
"Expected at most 4 messages with reasoning, got {}",
reasoning_count
);
}
#[test]
fn test_compression_metrics_all_methods() {
let micro = CompressionMetrics::new(CompressionMethod::Micro, 2000, 1500, 30, 25, 10);
assert_eq!(micro.tokens_saved, 500);
assert!(micro.summary().contains("MicroCompact"));
let auto = CompressionMetrics::new(CompressionMethod::Auto, 5000, 3000, 50, 10, 100);
assert_eq!(auto.tokens_saved, 2000);
assert!(auto.summary().contains("AutoCompact"));
let full = CompressionMetrics::new(CompressionMethod::Full, 10000, 5000, 100, 5, 200);
assert_eq!(full.tokens_saved, 5000);
assert!(full.summary().contains("FullCompact"));
}
#[test]
fn test_compression_metrics_zero_savings() {
let metrics = CompressionMetrics::new(
CompressionMethod::Micro,
1000,
1200, 10,
12,
5,
);
assert_eq!(metrics.tokens_saved, 0);
assert!(metrics.summary().contains("Saved 0 tokens"));
}
#[test]
fn test_micro_compact_preserves_system_message() {
let mut messages = vec![Message::system(
"Important system prompt that must be preserved",
)];
for i in 0..20 {
messages.push(Message::user(format!("User message {}", i)));
messages.push(Message::assistant(format!("Assistant response {}", i)));
}
micro_compact(&mut messages);
assert_eq!(messages[0].role, "system");
assert!(messages[0]
.content
.text()
.contains("Important system prompt"));
}
#[test]
fn test_orchestrator_run_micro() {
let mut orchestrator = CompressionOrchestrator::new();
let mut messages = vec![Message::system("System")];
for i in 0..15 {
messages.push(Message::user(format!("Q{}", i)));
messages.push(Message::assistant(format!("A{}", i)));
}
let metrics = orchestrator.run_micro(&mut messages);
assert_eq!(metrics.method, CompressionMethod::Micro);
assert_eq!(orchestrator.metrics_history().len(), 1);
assert_eq!(orchestrator.total_tokens_saved(), metrics.tokens_saved);
}
#[test]
fn test_orchestrator_multiple_micro_runs() {
let mut orchestrator = CompressionOrchestrator::new();
let mut messages1 = vec![Message::system("System")];
for i in 0..15 {
messages1.push(Message::user(format!("Q{}", i)));
messages1.push(Message::assistant(format!("A{}", i)));
}
orchestrator.run_micro(&mut messages1);
let mut messages2 = vec![Message::system("System")];
for i in 0..15 {
messages2.push(Message::user(format!("Q{}", i)));
messages2.push(Message::assistant(format!("A{}", i)));
}
orchestrator.run_micro(&mut messages2);
assert_eq!(orchestrator.metrics_history().len(), 2);
assert_eq!(
orchestrator.total_tokens_saved(),
orchestrator.metrics_history[0].tokens_saved + orchestrator.metrics_history[1].tokens_saved
);
}
#[test]
fn test_compression_metrics_llm_token_fields_round_trip() {
let metrics = CompressionMetrics::new(CompressionMethod::Auto, 1000, 600, 20, 8, 150)
.with_llm_tokens(42, 58);
assert_eq!(metrics.llm_input_tokens, 42);
assert_eq!(metrics.llm_output_tokens, 58);
let cloned = metrics.clone();
assert_eq!(cloned.llm_input_tokens, 42);
assert_eq!(cloned.llm_output_tokens, 58);
let default_metrics = CompressionMetrics::new(CompressionMethod::Micro, 100, 80, 5, 4, 1);
assert_eq!(default_metrics.llm_input_tokens, 0);
assert_eq!(default_metrics.llm_output_tokens, 0);
}