#![allow(clippy::float_cmp)]
use chaotic_semantic_memory::HVec10240;
use chaotic_semantic_memory::persistence::Persistence;
use chaotic_semantic_memory::singularity::Concept;
use std::collections::HashMap;
use tempfile::NamedTempFile;
const NS: &str = "_default";
fn make_concept(id: &str) -> Concept {
Concept {
id: id.to_string(),
vector: HVec10240::random(),
metadata: HashMap::new(),
created_at: 1,
modified_at: 1,
expires_at: None,
canonical_concept_ids: Vec::new(),
}
}
#[tokio::test]
async fn persistence_save_concept_overwrites_existing() {
let temp = NamedTempFile::new().unwrap();
let path = temp.path().to_str().unwrap();
let persistence = Persistence::new_local(path).await.unwrap();
let concept = make_concept("overwrite-test");
persistence.save_concept(NS, &concept).await.unwrap();
let updated = Concept {
id: "overwrite-test".to_string(),
vector: HVec10240::random(),
metadata: HashMap::new(),
created_at: 1,
modified_at: 2,
expires_at: None,
canonical_concept_ids: Vec::new(),
};
persistence.save_concept(NS, &updated).await.unwrap();
let loaded = persistence
.load_concept(NS, "overwrite-test")
.await
.unwrap();
assert!(loaded.is_some());
}
#[tokio::test]
async fn persistence_delete_nonexistent_concept() {
let temp = NamedTempFile::new().unwrap();
let path = temp.path().to_str().unwrap();
let persistence = Persistence::new_local(path).await.unwrap();
persistence.delete_concept(NS, "nonexistent").await.unwrap();
}
#[tokio::test]
async fn persistence_association_lifecycle() {
let temp = NamedTempFile::new().unwrap();
let path = temp.path().to_str().unwrap();
let persistence = Persistence::new_local(path).await.unwrap();
persistence
.save_concept(NS, &make_concept("from-id"))
.await
.unwrap();
persistence
.save_concept(NS, &make_concept("to-id"))
.await
.unwrap();
persistence
.save_association(NS, "from-id", "to-id", 0.8)
.await
.unwrap();
let assocs = persistence.load_associations(NS, "from-id").await.unwrap();
assert_eq!(assocs.len(), 1);
assert_eq!(assocs[0].0, "to-id");
assert_eq!(assocs[0].1, 0.8);
persistence
.delete_association(NS, "from-id", "to-id")
.await
.unwrap();
let assocs_after = persistence.load_associations(NS, "from-id").await.unwrap();
assert!(assocs_after.is_empty());
}
#[tokio::test]
async fn persistence_clear_associations() {
let temp = NamedTempFile::new().unwrap();
let path = temp.path().to_str().unwrap();
let persistence = Persistence::new_local(path).await.unwrap();
persistence
.save_concept(NS, &make_concept("clear-from"))
.await
.unwrap();
persistence
.save_concept(NS, &make_concept("clear-to1"))
.await
.unwrap();
persistence
.save_concept(NS, &make_concept("clear-to2"))
.await
.unwrap();
persistence
.save_association(NS, "clear-from", "clear-to1", 0.5)
.await
.unwrap();
persistence
.save_association(NS, "clear-from", "clear-to2", 0.6)
.await
.unwrap();
persistence
.clear_concept_associations(NS, "clear-from")
.await
.unwrap();
let assocs = persistence
.load_associations(NS, "clear-from")
.await
.unwrap();
assert!(assocs.is_empty());
}
#[tokio::test]
async fn persistence_list_all_concepts() {
let temp = NamedTempFile::new().unwrap();
let path = temp.path().to_str().unwrap();
let persistence = Persistence::new_local(path).await.unwrap();
persistence
.save_concept(NS, &make_concept("list-1"))
.await
.unwrap();
persistence
.save_concept(NS, &make_concept("list-2"))
.await
.unwrap();
persistence
.save_concept(NS, &make_concept("list-3"))
.await
.unwrap();
let concepts = persistence.load_all_concepts(NS).await.unwrap();
assert_eq!(concepts.len(), 3);
}
#[tokio::test]
async fn persistence_load_nonexistent_concept() {
let temp = NamedTempFile::new().unwrap();
let path = temp.path().to_str().unwrap();
let persistence = Persistence::new_local(path).await.unwrap();
let result = persistence
.load_concept(NS, "does-not-exist")
.await
.unwrap();
assert!(result.is_none());
}