use super::*;
use crate::Database;
use std::sync::Arc;
use tempfile::tempdir;
#[test]
fn test_agent_memory_new() {
let dir = tempdir().unwrap();
let db = Arc::new(Database::open(dir.path()).unwrap());
let memory = AgentMemory::new(Arc::clone(&db));
assert!(memory.is_ok(), "AgentMemory::new should succeed");
}
#[test]
fn test_agent_memory_semantic_access() {
let dir = tempdir().unwrap();
let db = Arc::new(Database::open(dir.path()).unwrap());
let memory = AgentMemory::new(Arc::clone(&db)).unwrap();
let semantic = memory.semantic();
assert!(semantic.collection_name().starts_with("_semantic"));
}
#[test]
fn test_agent_memory_episodic_access() {
let dir = tempdir().unwrap();
let db = Arc::new(Database::open(dir.path()).unwrap());
let memory = AgentMemory::new(Arc::clone(&db)).unwrap();
let episodic = memory.episodic();
assert!(episodic.collection_name().starts_with("_episodic"));
}
#[test]
fn test_agent_memory_procedural_access() {
let dir = tempdir().unwrap();
let db = Arc::new(Database::open(dir.path()).unwrap());
let memory = AgentMemory::new(Arc::clone(&db)).unwrap();
let procedural = memory.procedural();
assert!(procedural.collection_name().starts_with("_procedural"));
}
#[test]
fn test_agent_memory_shared_collections() {
let dir = tempdir().unwrap();
let db = Arc::new(Database::open(dir.path()).unwrap());
let memory1 = AgentMemory::new(Arc::clone(&db)).unwrap();
let memory2 = AgentMemory::new(Arc::clone(&db)).unwrap();
assert_eq!(
memory1.semantic().collection_name(),
memory2.semantic().collection_name()
);
}
#[test]
fn test_semantic_store_and_query() {
let dir = tempdir().unwrap();
let db = Arc::new(Database::open(dir.path()).unwrap());
let memory = AgentMemory::with_dimension(Arc::clone(&db), 4).unwrap();
let embedding = vec![1.0, 0.0, 0.0, 0.0];
memory
.semantic()
.store(1, "The sky is blue", &embedding)
.unwrap();
let results = memory.semantic().query(&embedding, 1).unwrap();
assert_eq!(results.len(), 1);
assert_eq!(results[0].0, 1); assert!(results[0].2.contains("blue")); }
#[test]
fn test_semantic_dimension_mismatch() {
let dir = tempdir().unwrap();
let db = Arc::new(Database::open(dir.path()).unwrap());
let memory = AgentMemory::with_dimension(Arc::clone(&db), 4).unwrap();
let bad_embedding = vec![1.0, 0.0]; let result = memory.semantic().store(1, "test", &bad_embedding);
assert!(result.is_err());
}
#[test]
fn test_dimension_mismatch_on_existing_collection() {
let dir = tempdir().unwrap();
let db = Arc::new(Database::open(dir.path()).unwrap());
let memory1 = AgentMemory::with_dimension(Arc::clone(&db), 4).unwrap();
assert_eq!(memory1.semantic().dimension(), 4);
let embedding = vec![1.0, 0.0, 0.0, 0.0];
memory1.semantic().store(1, "test", &embedding).unwrap();
let result = AgentMemory::with_dimension(Arc::clone(&db), 8);
assert!(result.is_err());
let memory2 = AgentMemory::with_dimension(Arc::clone(&db), 4);
assert!(memory2.is_ok());
}
#[test]
fn test_episodic_record_and_recent() {
let dir = tempdir().unwrap();
let db = Arc::new(Database::open(dir.path()).unwrap());
let memory = AgentMemory::with_dimension(Arc::clone(&db), 4).unwrap();
memory.episodic().record(1, "Event A", 1000, None).unwrap();
memory.episodic().record(2, "Event B", 2000, None).unwrap();
memory.episodic().record(3, "Event C", 3000, None).unwrap();
let events = memory.episodic().recent(2, None).unwrap();
assert_eq!(events.len(), 2);
assert_eq!(events[0].0, 3); assert_eq!(events[1].0, 2); }
#[test]
fn test_episodic_recall_similar() {
let dir = tempdir().unwrap();
let db = Arc::new(Database::open(dir.path()).unwrap());
let memory = AgentMemory::with_dimension(Arc::clone(&db), 4).unwrap();
let emb1 = vec![1.0, 0.0, 0.0, 0.0];
let emb2 = vec![0.0, 1.0, 0.0, 0.0];
memory
.episodic()
.record(1, "Similar to query", 1000, Some(&emb1))
.unwrap();
memory
.episodic()
.record(2, "Different event", 2000, Some(&emb2))
.unwrap();
let results = memory.episodic().recall_similar(&emb1, 2).unwrap();
assert!(!results.is_empty());
assert_eq!(results[0].0, 1); }
#[test]
fn test_procedural_learn_and_recall() {
let dir = tempdir().unwrap();
let db = Arc::new(Database::open(dir.path()).unwrap());
let memory = AgentMemory::with_dimension(Arc::clone(&db), 4).unwrap();
let embedding = vec![1.0, 0.0, 0.0, 0.0];
let steps = vec!["Step 1".to_string(), "Step 2".to_string()];
memory
.procedural()
.learn(1, "Test Procedure", &steps, Some(&embedding), 0.8)
.unwrap();
let results = memory.procedural().recall(&embedding, 1, 0.0).unwrap();
assert_eq!(results.len(), 1);
assert_eq!(results[0].id, 1);
assert_eq!(results[0].name, "Test Procedure");
assert_eq!(results[0].steps.len(), 2);
assert!((results[0].confidence - 0.8).abs() < 0.01);
}
#[test]
fn test_procedural_reinforce() {
let dir = tempdir().unwrap();
let db = Arc::new(Database::open(dir.path()).unwrap());
let memory = AgentMemory::with_dimension(Arc::clone(&db), 4).unwrap();
let embedding = vec![1.0, 0.0, 0.0, 0.0];
let steps = vec!["Step 1".to_string()];
memory
.procedural()
.learn(1, "Reinforce Test", &steps, Some(&embedding), 0.5)
.unwrap();
memory.procedural().reinforce(1, true).unwrap();
let results = memory.procedural().recall(&embedding, 1, 0.0).unwrap();
assert!((results[0].confidence - 0.6).abs() < 0.01); }
#[test]
fn test_procedural_min_confidence_filter() {
let dir = tempdir().unwrap();
let db = Arc::new(Database::open(dir.path()).unwrap());
let memory = AgentMemory::with_dimension(Arc::clone(&db), 4).unwrap();
let embedding = vec![1.0, 0.0, 0.0, 0.0];
let steps = vec!["Step".to_string()];
memory
.procedural()
.learn(1, "Low Confidence", &steps, Some(&embedding), 0.3)
.unwrap();
let results = memory.procedural().recall(&embedding, 1, 0.5).unwrap();
assert!(results.is_empty());
let results = memory.procedural().recall(&embedding, 1, 0.2).unwrap();
assert_eq!(results.len(), 1);
}
#[test]
fn test_with_eviction_config() {
let dir = tempdir().unwrap();
let db = Arc::new(Database::open(dir.path()).unwrap());
let config = EvictionConfig {
consolidation_age_threshold: 3600,
min_confidence_threshold: 0.5,
max_entries_per_cycle: 100,
};
let memory = AgentMemory::new(Arc::clone(&db))
.unwrap()
.with_eviction_config(config);
memory.semantic().store(1, "fact", &vec![0.0; 384]).unwrap();
assert!(memory.auto_expire().is_ok());
}
#[test]
fn test_with_snapshots() {
let dir = tempdir().unwrap();
let snap_dir = tempdir().unwrap();
let db = Arc::new(Database::open(dir.path()).unwrap());
let memory = AgentMemory::new(Arc::clone(&db))
.unwrap()
.with_snapshots(snap_dir.path().to_str().unwrap(), 5);
let version = memory.snapshot().unwrap();
assert_eq!(version, 1);
}
#[test]
fn test_set_ttl_functions() {
let dir = tempdir().unwrap();
let db = Arc::new(Database::open(dir.path()).unwrap());
let memory = AgentMemory::with_dimension(Arc::clone(&db), 4).unwrap();
let embedding = vec![1.0, 0.0, 0.0, 0.0];
memory.semantic().store(1, "sem fact", &embedding).unwrap();
memory
.episodic()
.record(2, "epi event", 1000, Some(&embedding))
.unwrap();
let steps = vec!["s1".to_string()];
memory
.procedural()
.learn(3, "proc", &steps, Some(&embedding), 0.9)
.unwrap();
memory.set_semantic_ttl(1, 3600);
memory.set_episodic_ttl(2, 7200);
memory.set_procedural_ttl(3, 1800);
let results = memory.semantic().query(&embedding, 1).unwrap();
assert_eq!(results.len(), 1);
}
#[test]
fn test_auto_expire_removes_expired() {
let dir = tempdir().unwrap();
let db = Arc::new(Database::open(dir.path()).unwrap());
let memory = AgentMemory::with_dimension(Arc::clone(&db), 4).unwrap();
let embedding = vec![1.0, 0.0, 0.0, 0.0];
memory
.semantic()
.store(1, "will expire", &embedding)
.unwrap();
memory.set_semantic_ttl(1, 0);
let result = memory.auto_expire().unwrap();
assert_eq!(result.semantic_expired, 1);
assert!(result.episodic_expired <= 1);
assert!(result.procedural_expired <= 1);
let results = memory.semantic().query(&embedding, 1).unwrap();
assert!(results.is_empty());
}
#[test]
fn test_auto_expire_nothing_expired() {
let dir = tempdir().unwrap();
let db = Arc::new(Database::open(dir.path()).unwrap());
let memory = AgentMemory::with_dimension(Arc::clone(&db), 4).unwrap();
let embedding = vec![1.0, 0.0, 0.0, 0.0];
memory.semantic().store(1, "keep me", &embedding).unwrap();
memory.set_semantic_ttl(1, 999_999);
let result = memory.auto_expire().unwrap();
assert_eq!(result.semantic_expired, 0);
assert_eq!(result.episodic_expired, 0);
assert_eq!(result.procedural_expired, 0);
assert_eq!(result.episodic_consolidated, 0);
}
#[test]
fn test_evict_low_confidence_procedures() {
let dir = tempdir().unwrap();
let db = Arc::new(Database::open(dir.path()).unwrap());
let memory = AgentMemory::with_dimension(Arc::clone(&db), 4).unwrap();
let embedding = vec![1.0, 0.0, 0.0, 0.0];
let steps = vec!["step".to_string()];
memory
.procedural()
.learn(1, "weak", &steps, Some(&embedding), 0.1)
.unwrap();
memory
.procedural()
.learn(2, "strong", &steps, Some(&embedding), 0.9)
.unwrap();
let evicted = memory.evict_low_confidence_procedures(0.5).unwrap();
assert_eq!(evicted, 1);
let remaining = memory.procedural().list_all().unwrap();
assert_eq!(remaining.len(), 1);
assert_eq!(remaining[0].id, 2);
}
#[test]
fn test_evict_low_confidence_none_evicted() {
let dir = tempdir().unwrap();
let db = Arc::new(Database::open(dir.path()).unwrap());
let memory = AgentMemory::with_dimension(Arc::clone(&db), 4).unwrap();
let embedding = vec![1.0, 0.0, 0.0, 0.0];
let steps = vec!["step".to_string()];
memory
.procedural()
.learn(1, "good", &steps, Some(&embedding), 0.8)
.unwrap();
memory
.procedural()
.learn(2, "also good", &steps, Some(&embedding), 0.7)
.unwrap();
let evicted = memory.evict_low_confidence_procedures(0.5).unwrap();
assert_eq!(evicted, 0);
}
#[test]
fn test_snapshot_round_trip() {
let dir = tempdir().unwrap();
let snap_dir = tempdir().unwrap();
let db = Arc::new(Database::open(dir.path()).unwrap());
let memory = AgentMemory::with_dimension(Arc::clone(&db), 4)
.unwrap()
.with_snapshots(snap_dir.path().to_str().unwrap(), 5);
let embedding = vec![1.0, 0.0, 0.0, 0.0];
memory.semantic().store(1, "fact one", &embedding).unwrap();
memory
.episodic()
.record(2, "event one", 1000, Some(&embedding))
.unwrap();
let steps = vec!["step1".to_string()];
memory
.procedural()
.learn(3, "proc one", &steps, Some(&embedding), 0.8)
.unwrap();
let version = memory.snapshot().unwrap();
assert!(version >= 1);
let loaded_version = memory.load_latest_snapshot().unwrap();
assert_eq!(loaded_version, version);
}
#[test]
fn test_snapshot_without_manager_errors() {
let dir = tempdir().unwrap();
let db = Arc::new(Database::open(dir.path()).unwrap());
let memory = AgentMemory::new(Arc::clone(&db)).unwrap();
assert!(memory.snapshot().is_err());
assert!(memory.load_latest_snapshot().is_err());
assert!(memory.list_snapshot_versions().is_err());
}
#[test]
fn test_list_snapshot_versions() {
let dir = tempdir().unwrap();
let snap_dir = tempdir().unwrap();
let db = Arc::new(Database::open(dir.path()).unwrap());
let memory = AgentMemory::with_dimension(Arc::clone(&db), 4)
.unwrap()
.with_snapshots(snap_dir.path().to_str().unwrap(), 10);
let embedding = vec![1.0, 0.0, 0.0, 0.0];
memory.semantic().store(1, "fact", &embedding).unwrap();
let v1 = memory.snapshot().unwrap();
let v2 = memory.snapshot().unwrap();
let versions = memory.list_snapshot_versions().unwrap();
assert!(versions.contains(&v1));
assert!(versions.contains(&v2));
assert_eq!(versions.len(), 2);
}
#[test]
fn test_consolidate_old_episodes_via_auto_expire() {
let dir = tempdir().unwrap();
let db = Arc::new(Database::open(dir.path()).unwrap());
let config = EvictionConfig {
consolidation_age_threshold: 1,
min_confidence_threshold: 0.1,
max_entries_per_cycle: 1000,
};
let memory = AgentMemory::with_dimension(Arc::clone(&db), 4)
.unwrap()
.with_eviction_config(config);
let embedding = vec![1.0, 0.0, 0.0, 0.0];
memory
.episodic()
.record(1, "ancient event", 100, Some(&embedding))
.unwrap();
let result = memory.auto_expire().unwrap();
assert_eq!(result.episodic_consolidated, 1);
let sem_results = memory.semantic().query(&embedding, 1).unwrap();
assert_eq!(sem_results.len(), 1);
assert!(sem_results[0].2.contains("ancient event"));
}