use hippmem_core::model::enums::ContentType;
use hippmem_core::model::unit::WriteContext;
use hippmem_engine::{Engine, EngineConfig, WriteMemoryInput};
use tempfile::tempdir;
fn ctx() -> WriteContext {
WriteContext {
conversation_id: Some(1),
session_id: Some(1),
project_id: None,
task_id: None,
user_id: None,
local_time: hippmem_core::time::Timestamp(1_700_000_000_000),
preceding_memory_ids: vec![],
source_refs: vec![],
}
}
#[test]
fn traverse_depth_one_finds_neighbors() {
let dir = tempdir().unwrap();
let engine = Engine::open(EngineConfig {
store_dir: dir.path().join("hippmem.redb"),
..Default::default()
})
.unwrap();
let out1 = engine
.write(WriteMemoryInput {
content: "The project uses Rust to develop backend database services".into(),
content_type: Some(ContentType::ProjectKnowledge),
context: ctx(),
importance_hint: Some(0.7),
source_refs: vec![],
})
.unwrap();
engine
.write(WriteMemoryInput {
content: "Building a high-performance database query engine with Rust".into(),
content_type: Some(ContentType::Decision),
context: ctx(),
importance_hint: Some(0.7),
source_refs: vec![],
})
.unwrap();
let output = engine
.traverse(hippmem_engine::TraverseInput::new(out1.memory_id))
.unwrap();
for node in &output.nodes {
assert!(node.depth >= 1, "depth should start at 1");
assert!(!node.content_preview.is_empty());
}
let ids: std::collections::HashSet<_> = output.nodes.iter().map(|n| n.id).collect();
assert_eq!(
ids.len(),
output.nodes.len(),
"should have no duplicate nodes"
);
engine.close().unwrap();
}
#[test]
fn traverse_depth_two_reaches_further() {
let dir = tempdir().unwrap();
let engine = Engine::open(EngineConfig {
store_dir: dir.path().join("hippmem.redb"),
..Default::default()
})
.unwrap();
let out_a = engine
.write(WriteMemoryInput {
content: "The project uses Rust to develop a backend database".into(),
content_type: Some(ContentType::ProjectKnowledge),
context: ctx(),
importance_hint: Some(0.7),
source_refs: vec![],
})
.unwrap();
let _out_b = engine
.write(WriteMemoryInput {
content: "Database query engine optimization plan".into(),
content_type: Some(ContentType::Decision),
context: ctx(),
importance_hint: Some(0.7),
source_refs: vec![],
})
.unwrap();
let _out_c = engine
.write(WriteMemoryInput {
content: "Query performance analysis tool development".into(),
content_type: Some(ContentType::UserStatement),
context: ctx(),
importance_hint: Some(0.7),
source_refs: vec![],
})
.unwrap();
let output = engine
.traverse(hippmem_engine::TraverseInput {
start_id: out_a.memory_id,
max_depth: 2,
direction: hippmem_engine::TraverseDirection::Outgoing,
link_types: None,
})
.unwrap();
for node in &output.nodes {
assert!(node.depth == 1 || node.depth == 2, "depth should be 1 or 2");
}
engine.close().unwrap();
}
#[test]
fn traverse_not_found_errors() {
let dir = tempdir().unwrap();
let engine = Engine::open(EngineConfig {
store_dir: dir.path().join("hippmem.redb"),
..Default::default()
})
.unwrap();
let result = engine.traverse(hippmem_engine::TraverseInput::new(
hippmem_core::ids::MemoryId(99999),
));
assert!(result.is_err());
engine.close().unwrap();
}
#[test]
fn traverse_no_cycle_infinite_loop() {
let dir = tempdir().unwrap();
let engine = Engine::open(EngineConfig {
store_dir: dir.path().join("hippmem.redb"),
..Default::default()
})
.unwrap();
let out_a = engine
.write(WriteMemoryInput {
content: "Using Rust to develop databases".into(),
content_type: Some(ContentType::ProjectKnowledge),
context: ctx(),
importance_hint: Some(0.7),
source_refs: vec![],
})
.unwrap();
let _out_b = engine
.write(WriteMemoryInput {
content: "Databases must be written in Rust".into(),
content_type: Some(ContentType::Decision),
context: ctx(),
importance_hint: Some(0.7),
source_refs: vec![],
})
.unwrap();
let output = engine
.traverse(hippmem_engine::TraverseInput {
start_id: out_a.memory_id,
max_depth: 5,
direction: hippmem_engine::TraverseDirection::Outgoing,
link_types: None,
})
.unwrap();
let unique_ids: std::collections::HashSet<_> = output.nodes.iter().map(|n| n.id).collect();
assert_eq!(
unique_ids.len(),
output.nodes.len(),
"should have no duplicate nodes (cycle protection)"
);
engine.close().unwrap();
}
#[test]
fn traverse_max_depth_clamped() {
let dir = tempdir().unwrap();
let engine = Engine::open(EngineConfig {
store_dir: dir.path().join("hippmem.redb"),
..Default::default()
})
.unwrap();
engine
.write(WriteMemoryInput {
content: "Rust database".into(),
content_type: Some(ContentType::ProjectKnowledge),
context: ctx(),
importance_hint: None,
source_refs: vec![],
})
.unwrap();
let output = engine
.traverse(hippmem_engine::TraverseInput {
start_id: hippmem_core::ids::MemoryId(1),
max_depth: 0,
..Default::default() })
.unwrap_or_else(|_| {
hippmem_engine::TraverseOutput {
nodes: vec![],
edges: vec![],
}
});
let _ = output.nodes.len();
engine.close().unwrap();
}