#[cfg(test)]
mod tests {
use super::super::pattern_types::{BatchProgress, BatchResult};
use do_memory_core::{
Pattern, PatternEffectiveness, TaskContext, episode::PatternId, types::OutcomeStats,
};
use tempfile::TempDir;
async fn create_test_storage() -> crate::Result<(crate::TursoStorage, TempDir)> {
let dir = TempDir::new().unwrap();
let db_path = dir.path().join("test.db");
let db = libsql::Builder::new_local(&db_path)
.build()
.await
.map_err(|e| {
do_memory_core::Error::Storage(format!("Failed to create test database: {}", e))
})?;
let storage = crate::TursoStorage::from_database(db)?;
storage.initialize_schema().await?;
Ok((storage, dir))
}
fn create_test_pattern(id_suffix: &str) -> Pattern {
Pattern::DecisionPoint {
id: PatternId::new_v4(),
condition: format!("test condition {}", id_suffix),
action: format!("test action {}", id_suffix),
outcome_stats: OutcomeStats {
success_count: 5,
failure_count: 1,
total_count: 6,
avg_duration_secs: 0.0,
},
context: TaskContext::default(),
effectiveness: PatternEffectiveness::default(),
}
}
#[tokio::test]
async fn test_store_patterns_batch_empty() {
let (storage, _dir) = create_test_storage().await.unwrap();
let result = storage.store_patterns_batch(vec![]).await;
assert!(result.is_ok());
}
#[tokio::test]
async fn test_store_patterns_batch_single() {
let (storage, _dir) = create_test_storage().await.unwrap();
let patterns = vec![create_test_pattern("1")];
let result = storage.store_patterns_batch(patterns).await;
assert!(result.is_ok());
}
#[tokio::test]
async fn test_store_patterns_batch_multiple() {
let (storage, _dir) = create_test_storage().await.unwrap();
let patterns = vec![
create_test_pattern("1"),
create_test_pattern("2"),
create_test_pattern("3"),
];
let result = storage.store_patterns_batch(patterns.clone()).await;
assert!(result.is_ok());
for pattern in &patterns {
let retrieved = storage.get_pattern(pattern.id()).await.unwrap();
assert!(retrieved.is_some());
}
}
#[tokio::test]
async fn test_update_patterns_batch() {
let (storage, _dir) = create_test_storage().await.unwrap();
let patterns = vec![create_test_pattern("1"), create_test_pattern("2")];
storage
.store_patterns_batch(patterns.clone())
.await
.unwrap();
let updated_patterns: Vec<Pattern> = patterns
.into_iter()
.map(|mut p| {
if let Pattern::DecisionPoint {
ref mut condition,
ref mut action,
ref mut outcome_stats,
..
} = p
{
*condition = format!("{} updated", condition);
*action = format!("{} updated", action);
outcome_stats.success_count = 10;
}
p
})
.collect();
let result = storage
.update_patterns_batch(updated_patterns.clone())
.await;
assert!(result.is_ok());
}
#[tokio::test]
async fn test_update_patterns_batch_nonexistent() {
let (storage, _dir) = create_test_storage().await.unwrap();
let patterns = vec![create_test_pattern("nonexistent")];
let result = storage.update_patterns_batch(patterns).await;
assert!(result.is_err());
}
#[tokio::test]
async fn test_store_patterns_batch_with_progress() {
let (storage, _dir) = create_test_storage().await.unwrap();
let patterns: Vec<Pattern> = (0..25)
.map(|i| create_test_pattern(&i.to_string()))
.collect();
let result = storage
.store_patterns_batch_with_progress(patterns, 10)
.await
.unwrap();
assert_eq!(result.total_processed, 25);
assert_eq!(result.succeeded, 25);
assert_eq!(result.failed, 0);
assert!(result.all_succeeded);
}
#[tokio::test]
async fn test_batch_progress_tracking() {
let progress = BatchProgress::new(100, 10);
assert_eq!(progress.total, 100);
assert_eq!(progress.total_batches, 10);
assert_eq!(progress.processed, 0);
assert!(!progress.is_complete());
let mut progress = progress;
progress.update(10, 10, 0);
assert_eq!(progress.processed, 10);
assert_eq!(progress.succeeded, 10);
assert_eq!(progress.current_batch, 1);
assert_eq!(progress.percent_complete(), 10.0);
}
#[tokio::test]
async fn test_batch_result_success() {
let result = BatchResult::success(10);
assert_eq!(result.total_processed, 10);
assert_eq!(result.succeeded, 10);
assert_eq!(result.failed, 0);
assert!(result.all_succeeded);
assert!(result.errors.is_empty());
}
#[tokio::test]
async fn test_batch_result_failure() {
let result = BatchResult::failure("test error".to_string());
assert_eq!(result.total_processed, 0);
assert_eq!(result.succeeded, 0);
assert!(!result.all_succeeded);
assert_eq!(result.errors.len(), 1);
}
#[tokio::test]
async fn test_get_patterns_batch_empty() {
let (storage, _dir) = create_test_storage().await.unwrap();
let result = storage.get_patterns_batch(&[]).await.unwrap();
assert_eq!(result.len(), 0);
}
#[tokio::test]
async fn test_get_patterns_batch_nonexistent() {
let (storage, _dir) = create_test_storage().await.unwrap();
let ids = vec![PatternId::new_v4(), PatternId::new_v4()];
let result = storage.get_patterns_batch(&ids).await.unwrap();
assert_eq!(result.len(), 2);
assert!(result[0].is_none());
assert!(result[1].is_none());
}
#[tokio::test]
async fn test_get_patterns_batch_multiple() {
let (storage, _dir) = create_test_storage().await.unwrap();
let patterns = vec![
create_test_pattern("1"),
create_test_pattern("2"),
create_test_pattern("3"),
];
let ids: Vec<PatternId> = patterns.iter().map(|p| p.id()).collect();
storage
.store_patterns_batch(patterns.clone())
.await
.unwrap();
let retrieved = storage.get_patterns_batch(&ids).await.unwrap();
assert_eq!(retrieved.len(), 3);
let retrieved_ids: Vec<PatternId> = retrieved
.iter()
.filter_map(|p| p.as_ref().map(|p| p.id()))
.collect();
assert_eq!(retrieved_ids, ids);
}
#[tokio::test]
async fn test_get_patterns_batch_partial() {
let (storage, _dir) = create_test_storage().await.unwrap();
let patterns = vec![create_test_pattern("1"), create_test_pattern("2")];
let mut ids: Vec<PatternId> = patterns.iter().map(|p| p.id()).collect();
ids.push(PatternId::new_v4());
storage.store_patterns_batch(patterns).await.unwrap();
let retrieved = storage.get_patterns_batch(&ids).await.unwrap();
assert_eq!(retrieved.len(), 3); let found_count = retrieved.iter().filter(|p| p.is_some()).count();
assert_eq!(found_count, 2);
}
#[tokio::test]
async fn test_delete_patterns_batch_empty() {
let (storage, _dir) = create_test_storage().await.unwrap();
let result = storage.delete_patterns_batch(vec![]).await.unwrap();
assert_eq!(result, 0);
}
#[tokio::test]
async fn test_delete_patterns_batch_nonexistent() {
let (storage, _dir) = create_test_storage().await.unwrap();
let ids = vec![PatternId::new_v4(), PatternId::new_v4()];
let result = storage.delete_patterns_batch(ids).await.unwrap();
assert_eq!(result, 0);
}
#[tokio::test]
async fn test_delete_patterns_batch_single() {
let (storage, _dir) = create_test_storage().await.unwrap();
let pattern = create_test_pattern("1");
let id = pattern.id();
storage
.store_patterns_batch(vec![pattern.clone()])
.await
.unwrap();
let retrieved = storage.get_pattern(id).await.unwrap();
assert!(retrieved.is_some());
let deleted = storage.delete_patterns_batch(vec![id]).await.unwrap();
assert_eq!(deleted, 1);
let retrieved = storage.get_pattern(id).await.unwrap();
assert!(retrieved.is_none());
}
#[tokio::test]
async fn test_delete_patterns_batch_multiple() {
let (storage, _dir) = create_test_storage().await.unwrap();
let patterns = vec![
create_test_pattern("1"),
create_test_pattern("2"),
create_test_pattern("3"),
];
let ids: Vec<PatternId> = patterns.iter().map(|p| p.id()).collect();
storage
.store_patterns_batch(patterns.clone())
.await
.unwrap();
for id in &ids {
let retrieved = storage.get_pattern(*id).await.unwrap();
assert!(retrieved.is_some());
}
let deleted = storage.delete_patterns_batch(ids.clone()).await.unwrap();
assert_eq!(deleted, 3);
for id in &ids {
let retrieved = storage.get_pattern(*id).await.unwrap();
assert!(retrieved.is_none());
}
}
#[tokio::test]
async fn test_delete_patterns_batch_partial() {
let (storage, _dir) = create_test_storage().await.unwrap();
let patterns = vec![create_test_pattern("1"), create_test_pattern("2")];
let mut ids: Vec<PatternId> = patterns.iter().map(|p| p.id()).collect();
ids.push(PatternId::new_v4());
storage.store_patterns_batch(patterns).await.unwrap();
let deleted = storage.delete_patterns_batch(ids).await.unwrap();
assert_eq!(deleted, 2);
}
#[tokio::test]
async fn test_transaction_rollback_on_error() {
let (storage, _dir) = create_test_storage().await.unwrap();
let patterns = vec![create_test_pattern("1"), create_test_pattern("2")];
storage
.store_patterns_batch(patterns.clone())
.await
.unwrap();
let invalid_patterns = vec![patterns[0].clone(), create_test_pattern("nonexistent")];
let result = storage.update_patterns_batch(invalid_patterns).await;
assert!(result.is_err());
let retrieved = storage.get_pattern(patterns[0].id()).await.unwrap();
assert!(retrieved.is_some());
}
#[tokio::test]
async fn test_batch_performance_improvement() {
let (storage, _dir) = create_test_storage().await.unwrap();
let patterns: Vec<Pattern> = (0..50)
.map(|i| create_test_pattern(&i.to_string()))
.collect();
let start = std::time::Instant::now();
storage
.store_patterns_batch(patterns.clone())
.await
.unwrap();
let batch_time = start.elapsed();
let individual_patterns = patterns.into_iter().take(10).collect::<Vec<_>>();
let start = std::time::Instant::now();
for pattern in &individual_patterns {
let new_pattern = create_test_pattern(&format!("individual_{}", pattern.id()));
storage.store_pattern(&new_pattern).await.unwrap();
}
let individual_time = start.elapsed();
let avg_individual = individual_time / 10;
let avg_batch = batch_time / 50;
println!(
"Batch avg: {:?}, Individual avg: {:?}",
avg_batch, avg_individual
);
assert!(avg_batch.as_millis() < 100, "Batch operation too slow");
}
}