use crate::core::engine::WarmCore;
use crate::core::entangled::EntangledHVec;
#[test]
fn test_determinism() {
let dir = tempfile::tempdir().unwrap();
let warm = WarmCore::new(1000, Some(dir.path().display().to_string()), None).unwrap();
let v1 = warm.encode_text("hello world");
let v2 = warm.encode_text("hello world");
assert!((v1.similarity(&v2) - 1.0).abs() < 0.0001);
}
#[test]
fn test_store_rejects_concurrent_engines_and_reopens_after_drop() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().display().to_string();
let first = WarmCore::new(1000, Some(path.clone()), None).unwrap();
let error = WarmCore::new(1000, Some(path.clone()), None)
.err()
.expect("a second writable engine must be rejected");
assert!(error.to_string().contains("already open"));
drop(first);
WarmCore::new(1000, Some(path), None).expect("store lock must release after teardown");
}
#[test]
fn test_from_dense_produces_sparse() {
let dense: Vec<f32> = (0..128).map(|i| (i as f32 - 64.0) / 64.0).collect();
let e = EntangledHVec::from_dense(&dense, 1000);
assert_eq!(e.dim, 1000);
let expected = 1000 / 256;
assert_eq!(e.indices.len(), expected);
}
#[test]
fn test_batch_memorize() {
let dir = tempfile::tempdir().unwrap();
let warm = WarmCore::new(10_000, Some(dir.path().to_string_lossy().to_string()), None).unwrap();
let items = vec![
("b1", "first batch item"),
("b2", "second batch item"),
("b3", "third batch item"),
];
for (id, text) in &items {
let vec = warm.encode_text(text);
warm.memorize(id.to_string(), vec).unwrap();
}
assert_eq!(warm.vector_count(), 3);
let q = warm.encode_text("first batch item");
let results = warm.query(&q, 3);
assert!(!results.is_empty());
}
#[test]
fn test_custom_concept_config() {
let dir = tempfile::tempdir().unwrap();
let mut config = crate::core::config::WarmConfig::default();
config.concepts.similarity_threshold = 0.5;
config.concepts.min_cluster_size = 5;
let warm = WarmCore::new(
10_000,
Some(dir.path().to_string_lossy().to_string()),
Some(config),
)
.unwrap();
for i in 0..10 {
let vec = warm.encode_text(&format!("config test document {}", i));
warm.memorize(format!("cfg_{}", i), vec).unwrap();
}
let concepts = warm.synthesize_concepts();
for c in &concepts {
assert!(
c.member_count >= 5,
"Min cluster size should be 5, got {}",
c.member_count
);
}
}
#[test]
fn test_factorize_returns_factors() {
let dir = tempfile::tempdir().unwrap();
let warm = WarmCore::new(10_000, Some(dir.path().to_string_lossy().to_string()), None).unwrap();
let colors = vec!["red", "blue", "green"];
let shapes = vec!["circle", "square", "triangle"];
for c in &colors {
let v = warm.encode_text(c);
warm.memorize(c.to_string(), v).unwrap();
}
for s in &shapes {
let v = warm.encode_text(s);
warm.memorize(s.to_string(), v).unwrap();
}
let red_vec = warm.encode_text("red");
let circle_vec = warm.encode_text("circle");
let product = red_vec.bind(&circle_vec);
let domains = vec![
colors
.iter()
.map(|s| warm.encode_text(s))
.collect::<Vec<EntangledHVec>>(),
shapes
.iter()
.map(|s| warm.encode_text(s))
.collect::<Vec<EntangledHVec>>(),
];
let results = warm.factorize_diffusion(&product, &domains, 20);
assert_eq!(results.len(), 2);
}
#[test]
fn test_concept_synthesis() {
let dir = tempfile::tempdir().unwrap();
let warm = WarmCore::new(1000, Some(dir.path().to_string_lossy().to_string()), None).unwrap();
let base = warm.encode_text("base concept");
for i in 0..20 {
let variant = if i == 0 {
base.clone()
} else {
let perturb = EntangledHVec::new_deterministic(1000, 10000 + i);
EntangledHVec::bundle(&[
base.clone(),
base.clone(),
base.clone(),
base.clone(),
perturb,
])
};
warm.memorize(format!("var_{}", i), variant).unwrap();
}
let concepts = warm.synthesize_concepts();
assert!(
!concepts.is_empty(),
"Should synthesize at least one concept"
);
assert!(
concepts[0].coherence > 0.7,
"Synthesized concept should have high coherence, got {}",
concepts[0].coherence
);
}
#[test]
fn test_concept_refinement_stability() {
let dir = tempfile::tempdir().unwrap();
let mut config = crate::core::config::WarmConfig::default();
config.concepts.similarity_threshold = 0.3;
config.concepts.min_cluster_size = 3;
let warm = WarmCore::new(
1000,
Some(dir.path().to_string_lossy().to_string()),
Some(config),
)
.unwrap();
let base_a = warm.encode_text("alpha cluster topic");
let base_b = warm.encode_text("beta different subject");
for i in 0..10 {
let perturb = EntangledHVec::new_deterministic(1000, 20000 + i);
let va = EntangledHVec::bundle(&[
base_a.clone(),
base_a.clone(),
base_a.clone(),
base_a.clone(),
perturb.clone(),
]);
warm.memorize(format!("a_{}", i), va).unwrap();
let perturb_b = EntangledHVec::new_deterministic(1000, 30000 + i);
let vb = EntangledHVec::bundle(&[
base_b.clone(),
base_b.clone(),
base_b.clone(),
base_b.clone(),
perturb_b,
]);
warm.memorize(format!("b_{}", i), vb).unwrap();
}
let concepts = warm.synthesize_concepts();
assert!(
!concepts.is_empty(),
"Should synthesize at least one concept"
);
for c in &concepts {
assert!(
c.stable,
"Concept with {} members should be stable after refinement",
c.member_count
);
assert!(
c.coherence > 0.5,
"Coherence should be decent after refinement, got {}",
c.coherence
);
}
}
#[test]
fn test_nsg_train_and_query() {
let dir = tempfile::tempdir().unwrap();
let mut config = crate::core::config::WarmConfig::default();
config.nsg.max_degree = 8;
config.nsg.ef_construction = 16;
let warm = WarmCore::new(
1000,
Some(dir.path().to_string_lossy().to_string()),
Some(config),
)
.unwrap();
for i in 0..50 {
let vec = warm.encode_text(&format!("nsg document {}", i));
warm.memorize(format!("nsg_{}", i), vec).unwrap();
}
assert!(!warm.nsg_trained());
warm.train_nsg().unwrap();
assert!(warm.nsg_trained());
let q = warm.encode_text("nsg document 0");
let results = warm.query(&q, 5);
assert!(!results.is_empty(), "NSG query should return results");
}
#[test]
fn test_nsg_maintenance_at_threshold() {
let dir = tempfile::tempdir().unwrap();
let mut config = crate::core::config::WarmConfig::default();
config.nsg.auto_threshold = 30;
config.nsg.max_degree = 8;
config.nsg.ef_construction = 16;
let warm = WarmCore::new(
1000,
Some(dir.path().to_string_lossy().to_string()),
Some(config),
)
.unwrap();
for i in 0..29 {
let vec = warm.encode_text(&format!("auto nsg {}", i));
warm.memorize(format!("ansg_{}", i), vec).unwrap();
}
assert!(!warm.nsg_trained());
let vec = warm.encode_text("auto nsg 29");
warm.memorize("ansg_29".to_string(), vec).unwrap();
assert!(
!warm.nsg_trained(),
"ingestion must not synchronously train"
);
assert!(warm.index_status().nsg_training_recommended);
warm.maintain_indices().unwrap();
assert!(warm.nsg_trained());
}
#[test]
fn test_adaptive_routing_e2e() {
let dir = tempfile::tempdir().unwrap();
let mut config = crate::core::config::WarmConfig::default();
config.nsg.max_degree = 8;
config.nsg.ef_construction = 16;
let warm = WarmCore::new(
1000,
Some(dir.path().to_string_lossy().to_string()),
Some(config),
)
.unwrap();
for i in 0..50 {
let vec = warm.encode_text(&format!("routing item {}", i));
warm.memorize(format!("rt_{}", i), vec).unwrap();
}
warm.train_nsg().unwrap();
assert!(warm.nsg_trained());
let q = warm.encode_text("routing item 0");
let results = warm.query(&q, 5);
assert!(
!results.is_empty(),
"Adaptive routing should return results via NSG"
);
}
#[test]
fn test_triplet_memorize_and_query() {
let dir = tempfile::tempdir().unwrap();
let warm = WarmCore::new(10_000, Some(dir.path().to_string_lossy().to_string()), None).unwrap();
warm.memorize_triplet(
"paris_capital".to_string(),
"Paris".to_string(),
"is_capital_of".to_string(),
"France".to_string(),
)
.unwrap();
warm.memorize_triplet(
"berlin_capital".to_string(),
"Berlin".to_string(),
"is_capital_of".to_string(),
"Germany".to_string(),
)
.unwrap();
warm.memorize_triplet(
"tokyo_capital".to_string(),
"Tokyo".to_string(),
"is_capital_of".to_string(),
"Japan".to_string(),
)
.unwrap();
let results = warm
.query_triplet("Paris".to_string(), "is_capital_of".to_string(), 3)
.unwrap();
assert!(!results.is_empty(), "Triplet query should return results");
assert!(
results.iter().any(|r| r.id == "paris_capital"),
"Paris triplet should appear in top-3 results"
);
}
#[test]
fn test_sequence_memorize_and_query() {
let dir = tempfile::tempdir().unwrap();
let warm = WarmCore::new(10_000, Some(dir.path().to_string_lossy().to_string()), None).unwrap();
warm.memorize_sequence(
"recipe_1".to_string(),
&[
"preheat oven".to_string(),
"mix ingredients".to_string(),
"pour into pan".to_string(),
"bake for thirty minutes".to_string(),
],
)
.unwrap();
warm.memorize_sequence(
"recipe_2".to_string(),
&[
"boil water".to_string(),
"add pasta".to_string(),
"drain and serve".to_string(),
],
)
.unwrap();
let q = warm.encode_text("preheat oven").permute(0);
let results = warm.query(&q, 2);
assert!(!results.is_empty(), "Sequence query should return results");
}
#[test]
fn test_scalar_query_ordering() {
let dir = tempfile::tempdir().unwrap();
let warm = WarmCore::new(10_000, Some(dir.path().to_string_lossy().to_string()), None).unwrap();
for i in 0..20 {
let val = i as f64 * 5.0;
warm.memorize_scalar(format!("temp_{}", i), val, 0.0, 100.0)
.unwrap();
}
let q = EntangledHVec::from_scalar(50.0, 0.0, 100.0, 10_000);
let results = warm.query(&q, 5);
assert!(!results.is_empty(), "Scalar query should return results");
let top_idx: usize = results[0]
.id
.strip_prefix("temp_")
.unwrap()
.parse()
.unwrap();
assert!(
(5..=15).contains(&top_idx),
"Top scalar result should be near value 50 (idx 10), got idx {}",
top_idx
);
}
#[test]
fn test_analyze_components() {
let dir = tempfile::tempdir().unwrap();
let warm = WarmCore::new(10_000, Some(dir.path().to_string_lossy().to_string()), None).unwrap();
for i in 0..30 {
let vec = warm.encode_text(&format!("component analysis document {}", i));
warm.memorize(format!("ca_{}", i), vec).unwrap();
}
let vec = warm.encode_text("component analysis document 0");
let results = warm.analyze_components(&vec);
assert!(
!results.is_empty(),
"analyze_components should return results"
);
assert!(
results.iter().all(|r| r.similarity > 0.05),
"All results should exceed similarity threshold"
);
}
#[test]
fn test_delete_existing() {
let dir = tempfile::tempdir().unwrap();
let warm = WarmCore::new(10_000, Some(dir.path().to_string_lossy().to_string()), None).unwrap();
let vec = warm.encode_text("hello");
warm.memorize("hello".to_string(), vec).unwrap();
assert_eq!(warm.vector_count(), 1);
assert!(warm.delete("hello").unwrap());
assert_eq!(warm.vector_count(), 0);
let q = warm.encode_text("hello");
let results = warm.query(&q, 5);
assert!(
results.is_empty(),
"Deleted vector should not appear in results"
);
}
#[test]
fn test_delete_nonexistent() {
let dir = tempfile::tempdir().unwrap();
let warm = WarmCore::new(10_000, Some(dir.path().display().to_string()), None).unwrap();
assert!(!warm.delete("no_such_id").unwrap());
}
#[test]
fn test_basic_persistence() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().to_string_lossy().to_string();
{
let warm = WarmCore::new(10_000, Some(path.clone()), None).unwrap();
let v = warm.encode_text("persist me");
warm.memorize("p1".to_string(), v).unwrap();
assert_eq!(warm.vector_count(), 1);
}
let warm = WarmCore::new(10_000, Some(path), None).unwrap();
assert_eq!(warm.vector_count(), 1);
}
#[test]
fn test_delete_persistence() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().to_string_lossy().to_string();
{
let warm = WarmCore::new(10_000, Some(path.clone()), None).unwrap();
let v1 = warm.encode_text("keep me");
let v2 = warm.encode_text("delete me");
warm.memorize("keep".to_string(), v1).unwrap();
warm.memorize("del".to_string(), v2).unwrap();
assert_eq!(warm.vector_count(), 2);
warm.delete("del").unwrap();
assert_eq!(warm.vector_count(), 1);
}
let warm = WarmCore::new(10_000, Some(path), None).unwrap();
assert_eq!(warm.vector_count(), 1);
let q = warm.encode_text("keep me");
let results = warm.query(&q, 5);
assert!(!results.is_empty(), "Kept vector should survive restart");
}
#[test]
fn test_delete_and_rememorize() {
let dir = tempfile::tempdir().unwrap();
let warm = WarmCore::new(10_000, Some(dir.path().to_string_lossy().to_string()), None).unwrap();
let v1 = warm.encode_text("version 1");
warm.memorize("doc".to_string(), v1).unwrap();
warm.delete("doc").unwrap();
let v2 = warm.encode_text("version 2");
warm.memorize("doc".to_string(), v2.clone()).unwrap();
assert_eq!(warm.vector_count(), 1);
let q = warm.encode_text("version 2");
let results = warm.query(&q, 1);
assert_eq!(results[0].id, "doc");
}
#[test]
fn test_compact_basic() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().to_string_lossy().to_string();
let warm = WarmCore::new(10_000, Some(path.clone()), None).unwrap();
for i in 0..50 {
let vec = warm.encode_text(&format!("item {}", i));
warm.memorize(format!("id_{}", i), vec).unwrap();
}
for i in 0..25 {
warm.delete(&format!("id_{}", i)).unwrap();
}
assert_eq!(warm.vector_count(), 25);
warm.compact().unwrap();
let q = warm.encode_text("item 30");
let results = warm.query(&q, 5);
assert!(!results.is_empty(), "Should find results after compaction");
assert_eq!(warm.vector_count(), 25);
}
#[test]
fn test_compact_persistence() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().to_string_lossy().to_string();
{
let warm = WarmCore::new(10_000, Some(path.clone()), None).unwrap();
for i in 0..20 {
let vec = warm.encode_text(&format!("doc {}", i));
warm.memorize(format!("d_{}", i), vec).unwrap();
}
for i in 0..10 {
warm.delete(&format!("d_{}", i)).unwrap();
}
warm.compact().unwrap();
}
let warm = WarmCore::new(10_000, Some(path), None).unwrap();
assert_eq!(warm.vector_count(), 10);
}
#[test]
fn test_multi_shard_insert_query() {
let dir = tempfile::tempdir().unwrap();
let mut config = crate::core::config::WarmConfig::default();
config.shard.enabled = true;
config.shard.shard_count = 4;
let warm = WarmCore::new(
10_000,
Some(dir.path().to_string_lossy().to_string()),
Some(config),
)
.unwrap();
for i in 0..100 {
let vec = warm.encode_text(&format!("shard document {}", i));
warm.memorize(format!("sd_{}", i), vec).unwrap();
}
assert_eq!(warm.vector_count(), 100);
let q = warm.encode_text("shard document 0");
let results = warm.query(&q, 5);
assert!(
!results.is_empty(),
"Multi-shard query should return results"
);
}
#[test]
fn test_multi_shard_delete() {
let dir = tempfile::tempdir().unwrap();
let mut config = crate::core::config::WarmConfig::default();
config.shard.enabled = true;
config.shard.shard_count = 4;
let warm = WarmCore::new(
10_000,
Some(dir.path().to_string_lossy().to_string()),
Some(config),
)
.unwrap();
for i in 0..20 {
let vec = warm.encode_text(&format!("item {}", i));
warm.memorize(format!("m_{}", i), vec).unwrap();
}
assert_eq!(warm.vector_count(), 20);
for i in 0..10 {
assert!(warm.delete(&format!("m_{}", i)).unwrap());
}
assert_eq!(warm.vector_count(), 10);
}
#[test]
fn test_auto_shard_trigger() {
let dir = tempfile::tempdir().unwrap();
let mut config = crate::core::config::WarmConfig::default();
config.shard.enabled = true;
config.shard.shard_count = 0; config.shard.auto_threshold = 50;
config.shard.target_shard_size = 25;
let warm = WarmCore::new(
10_000,
Some(dir.path().to_string_lossy().to_string()),
Some(config),
)
.unwrap();
for i in 0..50 {
let vec = warm.encode_text(&format!("auto shard {}", i));
warm.memorize(format!("as_{}", i), vec).unwrap();
}
assert_eq!(warm.vector_count(), 50);
warm.maintain_indices().unwrap();
let metadata: serde_json::Value =
serde_json::from_slice(&std::fs::read(dir.path().join("shard_meta.json")).unwrap())
.unwrap();
assert_eq!(metadata["shard_count"], 2);
let q = warm.encode_text("auto shard 0");
let results = warm.query(&q, 5);
assert!(!results.is_empty(), "Should find results after auto-shard");
}
#[test]
fn test_audit_records_operations() {
let dir = tempfile::tempdir().unwrap();
let mut config = crate::core::config::WarmConfig::default();
config.security.audit_enabled = true;
let warm = WarmCore::new(
10_000,
Some(dir.path().to_string_lossy().to_string()),
Some(config),
)
.unwrap();
let v = warm.encode_text("audit test");
warm.memorize("aud_1".to_string(), v).unwrap();
warm.delete("aud_1").unwrap();
let entries = warm.audit_since(0).unwrap();
assert_eq!(entries.len(), 2);
assert_eq!(entries[0].op, crate::core::audit::AuditOp::Memorize);
assert_eq!(entries[1].op, crate::core::audit::AuditOp::Delete);
}
#[test]
fn test_audit_disabled_returns_empty() {
let dir = tempfile::tempdir().unwrap();
let warm = WarmCore::new(10_000, Some(dir.path().display().to_string()), None).unwrap();
let entries = warm.audit_since(0).unwrap();
assert!(entries.is_empty());
}
#[test]
fn test_audit_compact_recorded() {
let dir = tempfile::tempdir().unwrap();
let mut config = crate::core::config::WarmConfig::default();
config.security.audit_enabled = true;
let warm = WarmCore::new(
10_000,
Some(dir.path().to_string_lossy().to_string()),
Some(config),
)
.unwrap();
let v = warm.encode_text("compact audit");
warm.memorize("ca_1".to_string(), v).unwrap();
warm.compact().unwrap();
let entries = warm.audit_since(0).unwrap();
assert!(entries
.iter()
.any(|e| e.op == crate::core::audit::AuditOp::Compact));
}
#[test]
fn test_graph_add_and_traverse() {
let dir = tempfile::tempdir().unwrap();
let warm = WarmCore::new(10_000, Some(dir.path().to_string_lossy().to_string()), None).unwrap();
for city in &["paris", "france", "europe"] {
let v = warm.encode_text(city);
warm.memorize(city.to_string(), v).unwrap();
}
warm.add_relation(&crate::core::types::Relation {
source_id: "paris".into(),
relation_type: "is_in".into(),
target_id: "france".into(),
properties: None,
valid_from: 0.0,
valid_to: 0.0,
})
.unwrap();
warm.add_relation(&crate::core::types::Relation {
source_id: "france".into(),
relation_type: "is_in".into(),
target_id: "europe".into(),
properties: None,
valid_from: 0.0,
valid_to: 0.0,
})
.unwrap();
assert_eq!(warm.relation_count(), 2);
let paths = warm.traverse("paris", Some("is_in"), 3, 0.0);
assert!(!paths.is_empty());
let targets: Vec<&str> = paths
.iter()
.flat_map(|p| p.hops.iter().map(|h| h.node_id.as_str()))
.collect();
assert!(targets.contains(&"france"));
assert!(targets.contains(&"europe"));
}
#[test]
fn test_graph_transitive_inference() {
let dir = tempfile::tempdir().unwrap();
let warm = WarmCore::new(10_000, Some(dir.path().to_string_lossy().to_string()), None).unwrap();
for name in &["a", "b", "c"] {
let v = warm.encode_text(name);
warm.memorize(name.to_string(), v).unwrap();
}
warm.declare_relation_type(crate::core::types::RelationType {
name: "contains".into(),
transitive: true,
symmetric: false,
})
.unwrap();
warm.add_relation(&crate::core::types::Relation {
source_id: "a".into(),
relation_type: "contains".into(),
target_id: "b".into(),
properties: None,
valid_from: 0.0,
valid_to: 0.0,
})
.unwrap();
warm.add_relation(&crate::core::types::Relation {
source_id: "b".into(),
relation_type: "contains".into(),
target_id: "c".into(),
properties: None,
valid_from: 0.0,
valid_to: 0.0,
})
.unwrap();
let paths = warm.traverse("a", Some("contains"), 3, 0.0);
let inferred = paths
.iter()
.find(|p| p.hops.len() == 1 && p.hops[0].node_id == "c");
assert!(inferred.is_some(), "Should infer transitive a->c");
}
#[test]
fn test_graph_persistence() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().to_string_lossy().to_string();
{
let warm = WarmCore::new(10_000, Some(path.clone()), None).unwrap();
let v = warm.encode_text("node_a");
warm.memorize("node_a".to_string(), v).unwrap();
warm.add_relation(&crate::core::types::Relation {
source_id: "node_a".into(),
relation_type: "links_to".into(),
target_id: "node_b".into(),
properties: None,
valid_from: 0.0,
valid_to: 0.0,
})
.unwrap();
}
let warm = WarmCore::new(10_000, Some(path), None).unwrap();
assert_eq!(warm.relation_count(), 1);
let out = warm.outgoing_relations("node_a", None, 0.0);
assert_eq!(out.len(), 1);
assert_eq!(out[0].target_id, "node_b");
}
#[test]
fn test_graph_temporal_query() {
let dir = tempfile::tempdir().unwrap();
let warm = WarmCore::new(10_000, Some(dir.path().to_string_lossy().to_string()), None).unwrap();
warm.add_relation(&crate::core::types::Relation {
source_id: "alice".into(),
relation_type: "works_at".into(),
target_id: "acme".into(),
properties: None,
valid_from: 1000.0,
valid_to: 2000.0,
})
.unwrap();
warm.add_relation(&crate::core::types::Relation {
source_id: "alice".into(),
relation_type: "works_at".into(),
target_id: "globex".into(),
properties: None,
valid_from: 2001.0,
valid_to: 0.0,
})
.unwrap();
let at_1500 = warm.outgoing_relations("alice", Some("works_at"), 1500.0);
assert_eq!(at_1500.len(), 1);
assert_eq!(at_1500[0].target_id, "acme");
let at_3000 = warm.outgoing_relations("alice", Some("works_at"), 3000.0);
assert_eq!(at_3000.len(), 1);
assert_eq!(at_3000[0].target_id, "globex");
}
#[test]
fn test_federated_query() {
let dir1 = tempfile::tempdir().unwrap();
let dir2 = tempfile::tempdir().unwrap();
let path1 = dir1.path().to_string_lossy().to_string();
let path2 = dir2.path().to_string_lossy().to_string();
{
let hms1 = WarmCore::new(10_000, Some(path1.clone()), None).unwrap();
let v = hms1.encode_text("federated doc alpha");
hms1.memorize("alpha".to_string(), v).unwrap();
}
{
let hms2 = WarmCore::new(10_000, Some(path2.clone()), None).unwrap();
let v = hms2.encode_text("federated doc beta");
hms2.memorize("beta".to_string(), v).unwrap();
}
let hms1 = WarmCore::new(10_000, Some(path1), None).unwrap();
let q = hms1.encode_text("federated doc");
let results = hms1.federated_query(&[path2], &q, 5).unwrap();
let ids: Vec<&str> = results.iter().map(|r| r.id.as_str()).collect();
assert!(ids.contains(&"alpha"), "Should find local result");
assert!(ids.contains(&"beta"), "Should find federated result");
}
#[test]
fn test_hopfield_query_e2e() {
let dir = tempfile::tempdir().unwrap();
let warm = WarmCore::new(10_000, Some(dir.path().to_string_lossy().to_string()), None).unwrap();
let topics = [
"quantum physics experiments in laboratories",
"medieval history of european kingdoms",
"tropical marine biology and coral reefs",
"classical piano compositions by beethoven",
"volcanic geology and tectonic plate movements",
"abstract algebra and group theory proofs",
"impressionist painting techniques with oils",
"arctic wildlife migration patterns observed",
"distributed computing consensus algorithms",
"ancient roman architecture and aqueducts",
];
for (i, topic) in topics.iter().enumerate() {
let v = warm.encode_text(topic);
warm.memorize(format!("doc_{}", i), v).unwrap();
}
let q = warm.encode_text("volcanic geology and tectonic plate movements");
let results = warm.query_hopfield(&q, 10);
assert!(!results.is_empty(), "Hopfield query should return results");
let top3_ids: Vec<&str> = results.iter().take(3).map(|r| r.id.as_str()).collect();
assert!(
top3_ids.contains(&"doc_4"),
"Exact match should be in top-3 Hopfield results, got {:?}",
top3_ids
);
}
#[test]
fn test_graph_compact_preserves_relations() {
let dir = tempfile::tempdir().unwrap();
let warm = WarmCore::new(10_000, Some(dir.path().to_string_lossy().to_string()), None).unwrap();
let v = warm.encode_text("compactable");
warm.memorize("node1".to_string(), v).unwrap();
warm.add_relation(&crate::core::types::Relation {
source_id: "node1".into(),
relation_type: "related".into(),
target_id: "node2".into(),
properties: None,
valid_from: 0.0,
valid_to: 0.0,
})
.unwrap();
warm.compact().unwrap();
assert_eq!(warm.relation_count(), 1);
assert_eq!(warm.vector_count(), 1);
}
#[test]
fn test_find_analogy() {
let dir = tempfile::tempdir().unwrap();
let warm = WarmCore::new(10_000, Some(dir.path().to_string_lossy().to_string()), None).unwrap();
for word in &[
"walking", "talking", "running", "walked", "talked", "runner",
] {
let v = warm.encode_text(word);
warm.memorize(word.to_string(), v).unwrap();
}
let results = warm.find_analogy("walking", "walked", "talking", 5);
assert!(!results.is_empty(), "Analogy should return results");
}
#[test]
fn test_ivf_index_persistence() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().to_string_lossy().to_string();
{
let mut config = crate::core::config::WarmConfig::default();
config.ivf.n_clusters = 8;
config.ivf.n_landmarks = 64;
config.ivf.d_reduced = 16;
config.ivf.n_probe = 8;
let warm = WarmCore::new(10_000, Some(path.clone()), Some(config)).unwrap();
for i in 0..200 {
let v = warm.encode_text(&format!("ivf persist {}", i));
warm.memorize(format!("ip_{}", i), v).unwrap();
}
warm.train_ivf().unwrap();
assert!(warm.ivf_trained());
}
let mut config = crate::core::config::WarmConfig::default();
config.ivf.n_clusters = 8;
config.ivf.n_landmarks = 64;
config.ivf.d_reduced = 16;
config.ivf.n_probe = 8;
let warm = WarmCore::new(10_000, Some(path), Some(config)).unwrap();
assert!(
warm.ivf_trained(),
"IVF should be loaded from disk on re-open"
);
let q = warm.encode_text("ivf persist 0");
let results = warm.query(&q, 5);
assert!(!results.is_empty(), "IVF query should work after reload");
}
#[test]
fn test_query_batch() {
let dir = tempfile::tempdir().unwrap();
let warm = WarmCore::new(10_000, Some(dir.path().to_string_lossy().to_string()), None).unwrap();
for i in 0..20 {
let v = warm.encode_text(&format!("batch query doc {}", i));
warm.memorize(format!("bq_{}", i), v).unwrap();
}
let queries: Vec<EntangledHVec> = (0..3)
.map(|i| warm.encode_text(&format!("batch query doc {}", i)))
.collect();
let batch_results = warm.query_batch(&queries, 3);
assert_eq!(
batch_results.len(),
3,
"Should return one result set per query"
);
for results in &batch_results {
assert!(!results.is_empty(), "Each query should return results");
}
}
#[test]
fn test_memorize_vector_through_core() {
let dir = tempfile::tempdir().unwrap();
let warm = WarmCore::new(10_000, Some(dir.path().to_string_lossy().to_string()), None).unwrap();
let dense: Vec<f32> = (0..128).map(|i| (i as f32 - 64.0) / 64.0).collect();
warm.memorize_vector("dense_1".to_string(), &dense).unwrap();
assert_eq!(warm.vector_count(), 1);
let q = EntangledHVec::from_dense(&dense, 10_000);
let results = warm.query(&q, 1);
assert_eq!(results[0].id, "dense_1");
}
#[test]
fn test_readability_through_core() {
let dir = tempfile::tempdir().unwrap();
let warm = WarmCore::new(10_000, Some(dir.path().display().to_string()), None).unwrap();
let metrics = warm.analyze_text("The cat sat on the mat.");
assert!(metrics.word_count > 0);
let score = warm.calculate_readability(&metrics);
assert!(
score > 50.0,
"Simple sentence should be highly readable (got {:.1})",
score
);
}
#[test]
fn test_nsg_index_persistence() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().to_string_lossy().to_string();
{
let mut config = crate::core::config::WarmConfig::default();
config.nsg.max_degree = 8;
config.nsg.ef_construction = 16;
let warm = WarmCore::new(10_000, Some(path.clone()), Some(config)).unwrap();
for i in 0..50 {
let v = warm.encode_text(&format!("persist nsg {}", i));
warm.memorize(format!("pn_{}", i), v).unwrap();
}
warm.train_nsg().unwrap();
assert!(warm.nsg_trained());
}
let mut config = crate::core::config::WarmConfig::default();
config.nsg.max_degree = 8;
config.nsg.ef_construction = 16;
let warm = WarmCore::new(10_000, Some(path), Some(config)).unwrap();
assert!(
warm.nsg_trained(),
"NSG should be loaded from disk on re-open"
);
let q = warm.encode_text("persist nsg 0");
let results = warm.query(&q, 5);
assert!(!results.is_empty(), "NSG query should work after reload");
}
#[test]
fn test_nsg_accuracy_matches_brute_force() {
let dir = tempfile::tempdir().unwrap();
let mut config = crate::core::config::WarmConfig::default();
config.nsg.max_degree = 8;
config.nsg.ef_construction = 16;
let warm = WarmCore::new(
10_000,
Some(dir.path().to_string_lossy().to_string()),
Some(config),
)
.unwrap();
for i in 0..50 {
let v = warm.encode_text(&format!("accuracy test {}", i));
warm.memorize(format!("acc_{}", i), v).unwrap();
}
let q = warm.encode_text("accuracy test 0");
let brute_results = warm.query(&q, 1);
warm.train_nsg().unwrap();
let nsg_results = warm.query(&q, 1);
assert_eq!(
brute_results[0].id, nsg_results[0].id,
"NSG top-1 should match brute-force top-1"
);
}
#[test]
fn test_multi_shard_results_from_multiple_shards() {
let dir = tempfile::tempdir().unwrap();
let mut config = crate::core::config::WarmConfig::default();
config.shard.enabled = true;
config.shard.shard_count = 4;
let warm = WarmCore::new(
10_000,
Some(dir.path().to_string_lossy().to_string()),
Some(config),
)
.unwrap();
for i in 0..100 {
let v = warm.encode_text(&format!("multi verify {}", i));
warm.memorize(format!("mv_{}", i), v).unwrap();
}
let q = warm.encode_text("multi verify");
let results = warm.query(&q, 20);
assert!(
results.len() >= 10,
"Should return many results from across shards"
);
let mut shard_ids: std::collections::HashSet<usize> = std::collections::HashSet::new();
for r in &results {
shard_ids.insert((crate::core::hash::fx64(r.id.as_bytes()) as usize) % 4);
}
assert!(
shard_ids.len() >= 2,
"Results should come from multiple shards (got {} distinct shards)",
shard_ids.len()
);
}
#[test]
fn test_compact_multi_shard() {
let dir = tempfile::tempdir().unwrap();
let mut config = crate::core::config::WarmConfig::default();
config.shard.enabled = true;
config.shard.shard_count = 2;
let warm = WarmCore::new(
10_000,
Some(dir.path().to_string_lossy().to_string()),
Some(config),
)
.unwrap();
for i in 0..30 {
let v = warm.encode_text(&format!("compact shard {}", i));
warm.memorize(format!("cs_{}", i), v).unwrap();
}
for i in 0..15 {
warm.delete(&format!("cs_{}", i)).unwrap();
}
assert_eq!(warm.vector_count(), 15);
warm.compact().unwrap();
assert_eq!(warm.vector_count(), 15);
let q = warm.encode_text("compact shard 20");
let results = warm.query(&q, 5);
assert!(
!results.is_empty(),
"Should find results after multi-shard compact"
);
}
#[test]
fn test_manual_ivf_train_and_query() {
let dir = tempfile::tempdir().unwrap();
let mut config = crate::core::config::WarmConfig::default();
config.ivf.n_clusters = 8;
config.ivf.n_landmarks = 64;
config.ivf.d_reduced = 16;
config.ivf.n_probe = 8;
let warm = WarmCore::new(
10000,
Some(dir.path().to_string_lossy().to_string()),
Some(config),
)
.unwrap();
for i in 0..200 {
let vec = warm.encode_text(&format!("document number {}", i));
warm.memorize(format!("doc_{}", i), vec).unwrap();
}
assert!(!warm.ivf_trained());
warm.train_ivf().unwrap();
assert!(warm.ivf_trained());
let q = warm.encode_text("document number 0");
let results = warm.query(&q, 10);
assert!(!results.is_empty(), "IVF query should return results");
}
#[test]
fn test_ivf_maintenance_at_threshold() {
let dir = tempfile::tempdir().unwrap();
let mut config = crate::core::config::WarmConfig::default();
config.ivf.enabled = true;
config.ivf.auto_threshold = 50;
config.ivf.n_clusters = 8;
config.ivf.n_landmarks = 64;
config.ivf.d_reduced = 16;
config.ivf.n_probe = 8;
let warm = WarmCore::new(
1000,
Some(dir.path().to_string_lossy().to_string()),
Some(config),
)
.unwrap();
for i in 0..49 {
let vec = warm.encode_text(&format!("auto item {}", i));
warm.memorize(format!("auto_{}", i), vec).unwrap();
}
assert!(
!warm.ivf_trained(),
"Should not be trained before threshold"
);
let vec = warm.encode_text("auto item 49");
warm.memorize("auto_49".to_string(), vec).unwrap();
assert!(!warm.ivf_trained());
assert!(warm.index_status().ivf_training_recommended);
warm.maintain_indices().unwrap();
assert!(warm.ivf_trained());
}