use do_memory_core::{
EvictionPolicy, ExecutionResult, ExecutionStep, MemoryConfig, SelfLearningMemory, TaskContext,
TaskOutcome, TaskType,
};
async fn create_test_episode(
memory: &SelfLearningMemory,
task_desc: &str,
num_steps: usize,
) -> uuid::Uuid {
let episode_id = memory
.start_episode(
task_desc.to_string(),
TaskContext::default(),
TaskType::Testing,
)
.await;
for i in 0..num_steps {
let mut step =
ExecutionStep::new(i + 1, format!("tool_{}", i % 6), format!("Test step {i}"));
step.result = Some(ExecutionResult::Success {
output: format!("Step {i} completed successfully"),
});
memory.log_step(episode_id, step).await;
}
episode_id
}
#[tokio::test]
async fn test_complete_episode_with_summary() {
let config = MemoryConfig {
quality_threshold: 0.3, enable_summarization: true,
summary_min_length: 50,
summary_max_length: 150,
..Default::default()
};
let memory = SelfLearningMemory::with_config(config);
let episode_id = create_test_episode(&memory, "Implement authentication feature", 10).await;
let outcome = TaskOutcome::Success {
verdict: "Authentication implemented successfully with tests".to_string(),
artifacts: vec!["auth.rs".to_string(), "auth_test.rs".to_string()],
};
let result = memory.complete_episode(episode_id, outcome).await;
assert!(result.is_ok(), "Episode completion should succeed");
let episode = memory.get_episode(episode_id).await.unwrap();
assert!(episode.is_complete(), "Episode should be marked complete");
assert!(episode.reward.is_some(), "Episode should have reward");
assert!(
episode.reflection.is_some(),
"Episode should have reflection"
);
}
#[tokio::test]
async fn test_complete_episode_with_capacity() {
let config = MemoryConfig {
quality_threshold: 0.3, max_episodes: Some(5), eviction_policy: Some(EvictionPolicy::LRU),
enable_summarization: false, ..Default::default()
};
let memory = SelfLearningMemory::with_config(config);
for i in 0..7 {
let episode_id = create_test_episode(
&memory,
&format!("Task {i}"),
8, )
.await;
let outcome = TaskOutcome::Success {
verdict: format!("Task {i} completed"),
artifacts: vec![],
};
memory
.complete_episode(episode_id, outcome)
.await
.expect("Episode completion should succeed");
}
let (total, completed, _) = memory.get_stats().await;
assert!(total <= 5, "Should have at most 5 episodes, found {total}");
assert_eq!(completed, total, "All stored episodes should be completed");
}
#[tokio::test]
async fn test_eviction_during_completion() {
let config = MemoryConfig {
quality_threshold: 0.3, max_episodes: Some(3),
eviction_policy: Some(EvictionPolicy::RelevanceWeighted),
enable_summarization: false,
..Default::default()
};
let memory = SelfLearningMemory::with_config(config);
for i in 0..3 {
let episode_id = create_test_episode(
&memory,
&format!("Low quality task {i}"),
6, )
.await;
memory
.complete_episode(
episode_id,
TaskOutcome::Success {
verdict: "Completed".to_string(),
artifacts: vec![],
},
)
.await
.unwrap();
}
let (total_before, _, _) = memory.get_stats().await;
assert_eq!(total_before, 3, "Should have 3 episodes before eviction");
let high_quality_id = create_test_episode(
&memory,
"High quality complex task",
15, )
.await;
memory
.complete_episode(
high_quality_id,
TaskOutcome::Success {
verdict: "High quality implementation with comprehensive testing".to_string(),
artifacts: vec!["impl.rs".to_string(), "tests.rs".to_string()],
},
)
.await
.unwrap();
let (total_after, _, _) = memory.get_stats().await;
assert!(
total_after <= 3,
"Should have at most 3 episodes after eviction, found {total_after}"
);
assert!(
memory.get_episode(high_quality_id).await.is_ok(),
"High-quality episode should not be evicted"
);
}
#[tokio::test]
async fn test_capacity_performance_overhead() {
use std::time::Instant;
let config_unlimited = MemoryConfig {
quality_threshold: 0.3, max_episodes: None, enable_summarization: false,
..Default::default()
};
let memory_unlimited = SelfLearningMemory::with_config(config_unlimited);
let start_unlimited = Instant::now();
for i in 0..5 {
let episode_id = create_test_episode(&memory_unlimited, &format!("Task {i}"), 8).await;
memory_unlimited
.complete_episode(
episode_id,
TaskOutcome::Success {
verdict: "Done".to_string(),
artifacts: vec![],
},
)
.await
.unwrap();
}
let duration_unlimited = start_unlimited.elapsed();
let config_limited = MemoryConfig {
quality_threshold: 0.3, max_episodes: Some(100), eviction_policy: Some(EvictionPolicy::RelevanceWeighted),
enable_summarization: false,
..Default::default()
};
let memory_limited = SelfLearningMemory::with_config(config_limited);
let start_limited = Instant::now();
for i in 0..5 {
let episode_id = create_test_episode(&memory_limited, &format!("Task {i}"), 8).await;
memory_limited
.complete_episode(
episode_id,
TaskOutcome::Success {
verdict: "Done".to_string(),
artifacts: vec![],
},
)
.await
.unwrap();
}
let duration_limited = start_limited.elapsed();
#[allow(clippy::cast_precision_loss)]
let overhead_ms = duration_limited
.saturating_sub(duration_unlimited)
.as_millis() as f64
/ 5.0;
println!("Unlimited: {duration_unlimited:?}");
println!("Limited: {duration_limited:?}");
println!("Average overhead per episode: {overhead_ms:.2} ms");
assert!(
overhead_ms <= 50.0,
"Capacity check overhead should be minimal, found {overhead_ms:.2} ms"
);
}
#[tokio::test]
async fn test_backward_compatibility_no_capacity() {
let config = MemoryConfig {
quality_threshold: 0.3, max_episodes: None, eviction_policy: None,
enable_summarization: false,
..Default::default()
};
let memory = SelfLearningMemory::with_config(config);
for i in 0..10 {
let episode_id = create_test_episode(&memory, &format!("Task {i}"), 8).await;
memory
.complete_episode(
episode_id,
TaskOutcome::Success {
verdict: format!("Task {i} completed"),
artifacts: vec![],
},
)
.await
.expect("Episode completion should succeed");
}
let (total, completed, _) = memory.get_stats().await;
assert_eq!(total, 10, "All 10 episodes should be stored");
assert_eq!(completed, 10, "All episodes should be completed");
}
#[tokio::test]
async fn test_summarization_with_capacity() {
let config = MemoryConfig {
quality_threshold: 0.3, max_episodes: Some(5),
eviction_policy: Some(EvictionPolicy::RelevanceWeighted),
enable_summarization: true,
summary_min_length: 50,
summary_max_length: 150,
..Default::default()
};
let memory = SelfLearningMemory::with_config(config);
for i in 0..7 {
let episode_id =
create_test_episode(&memory, &format!("Feature {i} implementation"), 10).await;
memory
.complete_episode(
episode_id,
TaskOutcome::Success {
verdict: format!("Feature {i} implemented with tests and docs"),
artifacts: vec![
format!("feature_{}.rs", i),
format!("feature_{}_test.rs", i),
],
},
)
.await
.expect("Episode completion should succeed");
}
let (total, completed, _) = memory.get_stats().await;
assert!(
total <= 5,
"Capacity should be enforced, found {total} episodes"
);
assert_eq!(completed, total, "All stored episodes should be completed");
}
#[tokio::test]
async fn test_eviction_preserves_high_quality() {
let config = MemoryConfig {
quality_threshold: 0.3, max_episodes: Some(3),
eviction_policy: Some(EvictionPolicy::RelevanceWeighted),
enable_summarization: false,
..Default::default()
};
let memory = SelfLearningMemory::with_config(config);
let high_quality_id =
create_test_episode(&memory, "Complex high-quality implementation", 15).await;
memory
.complete_episode(
high_quality_id,
TaskOutcome::Success {
verdict: "Excellent implementation with comprehensive testing and documentation"
.to_string(),
artifacts: vec![
"main.rs".to_string(),
"tests.rs".to_string(),
"docs.md".to_string(),
],
},
)
.await
.unwrap();
for i in 0..5 {
let low_quality_id = create_test_episode(&memory, &format!("Simple task {i}"), 6).await;
memory
.complete_episode(
low_quality_id,
TaskOutcome::Success {
verdict: "Done".to_string(),
artifacts: vec![],
},
)
.await
.unwrap();
}
let (total, _, _) = memory.get_stats().await;
assert!(total <= 3, "Should have at most 3 episodes, found {total}");
assert!(
memory.get_episode(high_quality_id).await.is_ok(),
"High-quality episode should be preserved during eviction"
);
}