use std::collections::HashMap;
use std::sync::Arc;
use tempfile::TempDir;
use velesdb_core::agent::AgentMemory;
use velesdb_core::{velesql::Parser, Database, SearchResult};
fn setup_agent_memory() -> (TempDir, Arc<Database>, AgentMemory) {
let dir = TempDir::new().expect("test: create temp dir");
let db = Arc::new(Database::open(dir.path()).expect("test: open database"));
let memory = AgentMemory::with_dimension(Arc::clone(&db), 4).expect("test: create AgentMemory");
(dir, db, memory)
}
fn db_execute(
db: &Database,
sql: &str,
params: &HashMap<String, serde_json::Value>,
) -> velesdb_core::Result<Vec<SearchResult>> {
let query = Parser::parse(sql).map_err(|e| velesdb_core::Error::Query(e.to_string()))?;
db.execute_query(&query, params)
}
fn vector_param(v: [f32; 4]) -> HashMap<String, serde_json::Value> {
let mut params = HashMap::new();
params.insert("v".to_string(), serde_json::json!(v));
params
}
#[test]
fn test_semantic_memory_velesql_query() {
let (_dir, db, memory) = setup_agent_memory();
memory
.semantic()
.store(1, "Paris is the capital of France", &[1.0, 0.0, 0.0, 0.0])
.expect("store semantic fact 1");
memory
.semantic()
.store(2, "Berlin is the capital of Germany", &[0.0, 1.0, 0.0, 0.0])
.expect("store semantic fact 2");
memory
.semantic()
.store(3, "Rome is the capital of Italy", &[0.0, 0.0, 1.0, 0.0])
.expect("store semantic fact 3");
let params = vector_param([1.0, 0.0, 0.0, 0.0]);
let results = db_execute(
&db,
"SELECT * FROM _semantic_memory WHERE vector NEAR $v LIMIT 5",
¶ms,
)
.expect("semantic vector search via Database::execute_query should succeed");
assert!(!results.is_empty(), "should return stored facts");
assert_eq!(
results[0].point.id, 1,
"closest to [1,0,0,0] should be point 1"
);
}
#[test]
fn test_semantic_memory_with_filter() {
let (_dir, db, memory) = setup_agent_memory();
memory
.semantic()
.store(1, "Paris fact", &[1.0, 0.0, 0.0, 0.0])
.expect("store 1");
memory
.semantic()
.store(2, "Berlin fact", &[0.0, 1.0, 0.0, 0.0])
.expect("store 2");
let params = HashMap::new();
let results = db_execute(
&db,
"SELECT * FROM _semantic_memory WHERE content = 'Paris fact' LIMIT 10",
¶ms,
)
.expect("payload filter on semantic memory should succeed");
assert_eq!(results.len(), 1);
assert_eq!(results[0].point.id, 1);
}
#[test]
fn test_episodic_memory_velesql_query() {
let (_dir, db, memory) = setup_agent_memory();
memory
.episodic()
.record(
1,
"User asked about weather",
1_700_000_000,
Some(&[1.0, 0.0, 0.0, 0.0]),
)
.expect("record 1");
memory
.episodic()
.record(
2,
"User asked about code",
1_700_000_100,
Some(&[0.0, 1.0, 0.0, 0.0]),
)
.expect("record 2");
let params = HashMap::new();
let results = db_execute(&db, "SELECT * FROM _episodic_memory LIMIT 10", ¶ms)
.expect("scan query on episodic memory should succeed");
assert_eq!(results.len(), 2, "should return 2 recorded events");
}
#[test]
fn test_episodic_memory_recent_via_sql() {
let (_dir, db, memory) = setup_agent_memory();
memory
.episodic()
.record(1, "early", 1_000_000, Some(&[1.0, 0.0, 0.0, 0.0]))
.expect("record 1");
memory
.episodic()
.record(2, "mid", 2_000_000, Some(&[0.0, 1.0, 0.0, 0.0]))
.expect("record 2");
memory
.episodic()
.record(3, "late", 3_000_000, Some(&[0.0, 0.0, 1.0, 0.0]))
.expect("record 3");
let params = HashMap::new();
let results = db_execute(
&db,
"SELECT * FROM _episodic_memory ORDER BY timestamp DESC LIMIT 5",
¶ms,
)
.expect("ORDER BY timestamp DESC should succeed");
assert_eq!(results.len(), 3);
let timestamps: Vec<i64> = results
.iter()
.filter_map(|r| {
r.point
.payload
.as_ref()
.and_then(|p| p.get("timestamp"))
.and_then(serde_json::Value::as_i64)
})
.collect();
assert!(
timestamps.windows(2).all(|w| w[0] >= w[1]),
"timestamps should be descending: {timestamps:?}"
);
}
#[test]
fn test_procedural_memory_velesql_query() {
let (_dir, db, memory) = setup_agent_memory();
memory
.procedural()
.learn(
1,
"greet_user",
&["say hello".to_string(), "ask name".to_string()],
Some(&[1.0, 0.0, 0.0, 0.0]),
0.9,
)
.expect("learn 1");
memory
.procedural()
.learn(
2,
"search_docs",
&["open search".to_string()],
Some(&[0.0, 1.0, 0.0, 0.0]),
0.5,
)
.expect("learn 2");
let params = HashMap::new();
let results = db_execute(&db, "SELECT * FROM _procedural_memory LIMIT 10", ¶ms)
.expect("scan query on procedural memory should succeed");
assert_eq!(results.len(), 2);
}
#[test]
fn test_agent_memory_show_collections_includes_internal() {
let (_dir, db, _memory) = setup_agent_memory();
let params = HashMap::new();
let results =
db_execute(&db, "SHOW COLLECTIONS", ¶ms).expect("SHOW COLLECTIONS should succeed");
let names: Vec<&str> = results
.iter()
.filter_map(|r| {
r.point
.payload
.as_ref()
.and_then(|p| p.get("name"))
.and_then(serde_json::Value::as_str)
})
.collect();
assert!(
names.contains(&"_semantic_memory"),
"SHOW COLLECTIONS should include _semantic_memory, got: {names:?}"
);
assert!(
names.contains(&"_episodic_memory"),
"SHOW COLLECTIONS should include _episodic_memory, got: {names:?}"
);
assert!(
names.contains(&"_procedural_memory"),
"SHOW COLLECTIONS should include _procedural_memory, got: {names:?}"
);
}
#[test]
fn test_user_expires_at_metadata_is_not_interpreted_as_ttl() {
let dir = TempDir::new().expect("test: create temp dir");
{
let db = Arc::new(Database::open(dir.path()).expect("test: open database"));
let memory =
AgentMemory::with_dimension(Arc::clone(&db), 4).expect("test: create AgentMemory");
let mut meta = serde_json::Map::new();
meta.insert("expires_at".to_string(), serde_json::json!(1_000_000_u64));
memory
.semantic()
.store_with_metadata(1, "offer expired yesterday", &[1.0, 0.0, 0.0, 0.0], &meta)
.expect("store fact with business expires_at metadata");
}
let db = Arc::new(Database::open(dir.path()).expect("test: reopen database"));
let memory = AgentMemory::with_dimension(Arc::clone(&db), 4).expect("test: reopen AgentMemory");
let stats = memory.auto_expire().expect("auto_expire should succeed");
assert_eq!(
stats.semantic_expired, 0,
"user expires_at metadata must not expire the fact"
);
let results = memory
.semantic()
.query(&[1.0, 0.0, 0.0, 0.0], 5)
.expect("query semantic memory");
assert!(
results.iter().any(|r| r.0 == 1),
"fact must survive reopen + auto_expire"
);
}
#[test]
fn test_expired_ttl_is_reclaimed_by_auto_expire_after_reopen() {
let dir = TempDir::new().expect("test: create temp dir");
{
let db = Arc::new(Database::open(dir.path()).expect("test: open database"));
let memory =
AgentMemory::with_dimension(Arc::clone(&db), 4).expect("test: create AgentMemory");
memory
.semantic()
.store(1, "ephemeral fact", &[1.0, 0.0, 0.0, 0.0])
.expect("store fact");
memory
.semantic()
.set_ttl_durable(1, 0)
.expect("persist immediate expiry");
}
let db = Arc::new(Database::open(dir.path()).expect("test: reopen database"));
let memory = AgentMemory::with_dimension(Arc::clone(&db), 4).expect("test: reopen AgentMemory");
assert!(
memory
.semantic()
.get(1)
.expect("get should succeed")
.is_none(),
"expired fact must be invisible after reopen"
);
let stats = memory.auto_expire().expect("auto_expire should succeed");
assert_eq!(
stats.semantic_expired, 1,
"auto_expire must reclaim the expired-but-unswept fact (storage leak guard)"
);
let collection = db
.get_vector_collection("_semantic_memory")
.expect("semantic collection exists");
assert_eq!(
collection.len(),
0,
"the expired point's storage must be physically reclaimed"
);
}