use super::{AdjacencyHelpers, AdjacencyIterator};
use crate::backend::native::graph_file::GraphFile;
use crate::backend::native::node_store::NodeStore;
use crate::backend::native::types::*;
#[cfg(test)]
fn create_test_graph_file() -> (GraphFile, tempfile::NamedTempFile) {
let temp_file = tempfile::NamedTempFile::new().unwrap();
let path = temp_file.path();
let graph_file = GraphFile::create(path).unwrap();
(graph_file, temp_file)
}
#[test]
fn test_adjacency_iterator_empty() {
let (mut graph_file, _temp_file) = create_test_graph_file();
let node = NodeRecord::new(
1,
"Test".to_string(),
"node1".to_string(),
serde_json::json!({}),
);
{
let mut node_store = NodeStore::new(&mut graph_file);
node_store.write_node(&node).unwrap();
}
let iterator = AdjacencyIterator::new_outgoing(&mut graph_file, 1).unwrap();
assert_eq!(iterator.total_count(), 0);
assert!(iterator.is_complete());
let iterator = AdjacencyIterator::new_incoming(&mut graph_file, 1).unwrap();
assert_eq!(iterator.total_count(), 0);
assert!(iterator.is_complete());
}
#[test]
fn test_adjacency_validation() {
let (mut graph_file, _temp_file) = create_test_graph_file();
let mut node_store = NodeStore::new(&mut graph_file);
let node = NodeRecord::new(
1,
"Test".to_string(),
"node1".to_string(),
serde_json::json!({}),
);
node_store.write_node(&node).unwrap();
let result = AdjacencyHelpers::validate_node_adjacency(&mut graph_file, 1);
assert!(result.is_ok());
}
#[cfg(test)]
mod linear_detector_tests {
use super::super::{LinearDetector, TraversalPattern};
#[test]
fn test_linear_detector_chain() {
let mut detector = LinearDetector::new();
let pattern = detector.observe(1, 1);
assert_eq!(
pattern,
TraversalPattern::Unknown,
"Step 1 should be Unknown"
);
assert_eq!(
detector.current_pattern(),
TraversalPattern::Unknown,
"Current pattern at step 1"
);
assert!(
!detector.is_linear_confirmed(),
"Should not be confirmed at step 1"
);
assert!((detector.confidence() - 1.0 / 3.0).abs() < f64::EPSILON);
let pattern = detector.observe(2, 1);
assert_eq!(
pattern,
TraversalPattern::Unknown,
"Step 2 should be Unknown"
);
assert_eq!(
detector.current_pattern(),
TraversalPattern::Unknown,
"Current pattern at step 2"
);
assert!(
!detector.is_linear_confirmed(),
"Should not be confirmed at step 2"
);
assert!((detector.confidence() - 2.0 / 3.0).abs() < f64::EPSILON);
let pattern = detector.observe(3, 1);
assert_eq!(pattern, TraversalPattern::Linear, "Step 3 should be Linear");
assert_eq!(
detector.current_pattern(),
TraversalPattern::Linear,
"Current pattern at step 3"
);
assert!(
detector.is_linear_confirmed(),
"Should be confirmed at step 3"
);
assert_eq!(
detector.confidence(),
1.0,
"Confidence should be 1.0 at step 3"
);
let pattern = detector.observe(4, 1);
assert_eq!(
pattern,
TraversalPattern::Linear,
"Step 4 should stay Linear"
);
assert!(detector.is_linear_confirmed());
let pattern = detector.observe(5, 1);
assert_eq!(
pattern,
TraversalPattern::Linear,
"Step 5 should stay Linear"
);
assert!(detector.is_linear_confirmed());
}
#[test]
fn test_linear_detector_star() {
let mut detector = LinearDetector::new();
let pattern = detector.observe(1, 4);
assert_eq!(
pattern,
TraversalPattern::Branching,
"Degree 4 should trigger Branching immediately"
);
assert_eq!(
detector.current_pattern(),
TraversalPattern::Branching,
"Current pattern should be Branching"
);
assert_eq!(
detector.confidence(),
0.0,
"Confidence should be 0.0 for Branching"
);
assert!(
!detector.is_linear_confirmed(),
"Branching pattern should never be confirmed Linear"
);
let pattern = detector.observe(2, 1);
assert_eq!(
pattern,
TraversalPattern::Branching,
"Branching is terminal state"
);
let pattern = detector.observe(3, 1);
assert_eq!(pattern, TraversalPattern::Branching, "Still Branching");
assert_eq!(detector.confidence(), 0.0, "Confidence remains 0.0");
}
#[test]
fn test_linear_detector_diamond() {
let mut detector = LinearDetector::new();
let pattern = detector.observe(1, 1);
assert_eq!(
pattern,
TraversalPattern::Unknown,
"First degree-1 should be Unknown"
);
assert!(!detector.is_linear_confirmed());
let pattern = detector.observe(2, 1);
assert_eq!(
pattern,
TraversalPattern::Unknown,
"Second degree-1 should still be Unknown"
);
assert!(!detector.is_linear_confirmed());
let pattern = detector.observe(3, 2);
assert_eq!(
pattern,
TraversalPattern::Branching,
"Degree 2 should trigger Branching"
);
assert_eq!(detector.current_pattern(), TraversalPattern::Branching);
assert!(!detector.is_linear_confirmed());
assert_eq!(detector.confidence(), 0.0);
}
#[test]
fn test_linear_detector_diamond_accumulating_then_branching() {
let mut detector = LinearDetector::new();
assert_eq!(detector.observe(1, 1), TraversalPattern::Unknown);
assert_eq!(detector.observe(2, 1), TraversalPattern::Unknown);
assert_eq!(detector.observe(3, 2), TraversalPattern::Branching);
assert!(!detector.is_linear_confirmed());
assert_eq!(detector.confidence(), 0.0);
}
#[test]
fn test_linear_detector_tree() {
let mut detector = LinearDetector::new();
let pattern = detector.observe(1, 3);
assert_eq!(
pattern,
TraversalPattern::Branching,
"Root degree 3 triggers Branching"
);
assert!(!detector.is_linear_confirmed());
}
#[test]
fn test_linear_detector_tree_depth_first() {
let mut detector = LinearDetector::new();
assert_eq!(detector.observe(1, 1), TraversalPattern::Unknown);
assert!((detector.confidence() - 1.0 / 3.0).abs() < f64::EPSILON);
assert_eq!(detector.observe(2, 1), TraversalPattern::Unknown);
assert!((detector.confidence() - 2.0 / 3.0).abs() < f64::EPSILON);
assert_eq!(detector.observe(3, 2), TraversalPattern::Branching);
assert!(!detector.is_linear_confirmed());
assert_eq!(detector.confidence(), 0.0);
}
#[test]
fn test_linear_detector_confidence() {
let mut detector = LinearDetector::new();
assert_eq!(
detector.confidence(),
0.0,
"Initial confidence should be 0.0"
);
assert_eq!(
detector.current_pattern(),
TraversalPattern::Unknown,
"Initial pattern should be Unknown"
);
let pattern = detector.observe(1, 1);
assert_eq!(pattern, TraversalPattern::Unknown);
assert!((detector.confidence() - 1.0 / 3.0).abs() < f64::EPSILON);
assert_eq!(detector.current_pattern(), TraversalPattern::Unknown);
let pattern = detector.observe(2, 1);
assert_eq!(pattern, TraversalPattern::Unknown);
assert!((detector.confidence() - 2.0 / 3.0).abs() < f64::EPSILON);
assert_eq!(detector.current_pattern(), TraversalPattern::Unknown);
let pattern = detector.observe(3, 1);
assert_eq!(pattern, TraversalPattern::Linear);
assert_eq!(detector.confidence(), 1.0);
assert_eq!(detector.current_pattern(), TraversalPattern::Linear);
let pattern = detector.observe(4, 1);
assert_eq!(pattern, TraversalPattern::Linear);
assert_eq!(detector.confidence(), 1.0);
let pattern = detector.observe(5, 1);
assert_eq!(pattern, TraversalPattern::Linear);
assert_eq!(detector.confidence(), 1.0);
}
#[test]
fn test_linear_detector_reset() {
let mut detector = LinearDetector::new();
detector.observe(1, 1);
detector.observe(2, 1);
detector.observe(3, 1);
assert!(
detector.is_linear_confirmed(),
"Should be Linear after 3 degree-1 steps"
);
assert_eq!(detector.confidence(), 1.0, "Confidence should be 1.0");
assert_eq!(
detector.current_pattern(),
TraversalPattern::Linear,
"Pattern should be Linear"
);
detector.reset();
assert!(
!detector.is_linear_confirmed(),
"Should not be confirmed after reset"
);
assert_eq!(
detector.confidence(),
0.0,
"Confidence should be 0.0 after reset"
);
assert_eq!(
detector.current_pattern(),
TraversalPattern::Unknown,
"Pattern should be Unknown after reset"
);
let pattern = detector.observe(1, 1);
assert_eq!(
pattern,
TraversalPattern::Unknown,
"First observation after reset"
);
assert!((detector.confidence() - 1.0 / 3.0).abs() < f64::EPSILON);
detector.observe(2, 1);
detector.observe(3, 1);
assert!(
detector.is_linear_confirmed(),
"Should confirm Linear again"
);
}
#[test]
fn test_linear_threshold_custom() {
let detector = LinearDetector::with_threshold(5);
assert_eq!(detector.confidence(), 0.0);
let mut detector = LinearDetector::with_threshold(5);
let pattern = detector.observe(1, 1);
assert_eq!(pattern, TraversalPattern::Unknown);
assert!((detector.confidence() - 1.0 / 5.0).abs() < f64::EPSILON);
assert!(!detector.is_linear_confirmed());
let pattern = detector.observe(2, 1);
assert_eq!(pattern, TraversalPattern::Unknown);
assert!((detector.confidence() - 2.0 / 5.0).abs() < f64::EPSILON);
assert!(!detector.is_linear_confirmed());
let pattern = detector.observe(3, 1);
assert_eq!(pattern, TraversalPattern::Unknown);
assert!((detector.confidence() - 3.0 / 5.0).abs() < f64::EPSILON);
assert!(!detector.is_linear_confirmed());
let pattern = detector.observe(4, 1);
assert_eq!(pattern, TraversalPattern::Unknown);
assert!((detector.confidence() - 4.0 / 5.0).abs() < f64::EPSILON);
assert!(!detector.is_linear_confirmed());
let pattern = detector.observe(5, 1);
assert_eq!(
pattern,
TraversalPattern::Linear,
"Should confirm Linear at threshold=5"
);
assert_eq!(detector.confidence(), 1.0);
assert!(detector.is_linear_confirmed());
}
#[test]
fn test_linear_detector_dead_end() {
let mut detector = LinearDetector::new();
let pattern = detector.observe(1, 0);
assert_eq!(
pattern,
TraversalPattern::Unknown,
"Degree 0 should be Unknown"
);
assert_eq!(
detector.current_pattern(),
TraversalPattern::Unknown,
"Current pattern should be Unknown"
);
assert_eq!(detector.confidence(), 0.0, "Confidence should be 0.0");
assert!(
!detector.is_linear_confirmed(),
"Should not be confirmed with degree 0"
);
let pattern = detector.observe(2, 0);
assert_eq!(pattern, TraversalPattern::Unknown);
assert_eq!(detector.confidence(), 0.0);
let pattern = detector.observe(3, 1);
assert_eq!(pattern, TraversalPattern::Unknown);
assert!((detector.confidence() - 1.0 / 3.0).abs() < f64::EPSILON);
}
#[test]
fn test_linear_detector_dead_end_breaks_chain() {
let mut detector = LinearDetector::new();
assert_eq!(detector.observe(1, 1), TraversalPattern::Unknown);
assert!((detector.confidence() - 1.0 / 3.0).abs() < f64::EPSILON);
assert_eq!(detector.observe(2, 0), TraversalPattern::Unknown);
assert!((detector.confidence() - 1.0 / 3.0).abs() < f64::EPSILON);
assert_eq!(detector.observe(3, 1), TraversalPattern::Unknown);
assert!((detector.confidence() - 2.0 / 3.0).abs() < f64::EPSILON);
assert_eq!(detector.observe(4, 1), TraversalPattern::Linear);
assert!(detector.is_linear_confirmed());
}
}
#[cfg(test)]
mod sequential_read_tests {
use super::super::SequentialReadBuffer;
use super::*;
fn create_test_graph_file_with_nodes(node_count: u64) -> (GraphFile, tempfile::NamedTempFile) {
let (mut graph_file, temp_file) = create_test_graph_file();
let mut node_store = NodeStore::new(&mut graph_file);
for i in 1..=node_count {
let node = NodeRecord::new(
i as i64,
"TestNode".to_string(),
format!("node_{}", i),
serde_json::json!({"index": i}),
);
node_store.write_node(&node).unwrap();
}
(graph_file, temp_file)
}
#[test]
fn test_read_slots_batch_basic() {
let (mut graph_file, _temp_file) = create_test_graph_file_with_nodes(10);
let mut node_store = NodeStore::new(&mut graph_file);
let result = node_store.read_slots_batch(1, 3);
assert!(result.is_ok());
let nodes = result.unwrap();
assert_eq!(nodes.len(), 3);
assert_eq!(nodes[0].id, 1);
assert_eq!(nodes[1].id, 2);
assert_eq!(nodes[2].id, 3);
}
#[test]
fn test_read_slots_batch_bounds_clamping() {
let (mut graph_file, _temp_file) = create_test_graph_file_with_nodes(5);
let mut node_store = NodeStore::new(&mut graph_file);
let result = node_store.read_slots_batch(1, 10);
assert!(result.is_ok());
let nodes = result.unwrap();
assert_eq!(nodes.len(), 5); }
#[test]
fn test_read_slots_batch_invalid_start() {
let (mut graph_file, _temp_file) = create_test_graph_file_with_nodes(10);
let mut node_store = NodeStore::new(&mut graph_file);
let result = node_store.read_slots_batch(0, 3);
assert!(result.is_err());
let result = node_store.read_slots_batch(99, 3);
assert!(result.is_err());
}
#[test]
fn test_sequential_buffer_prefetch_integration() {
let (mut graph_file, _temp_file) = create_test_graph_file_with_nodes(20);
let mut buffer = SequentialReadBuffer::new();
let result = buffer.prefetch_from(&mut graph_file, 5);
assert!(result.is_ok());
assert!(buffer.contains(5));
assert!(buffer.contains(6));
assert!(buffer.contains(12));
assert!(!buffer.contains(4));
assert!(!buffer.contains(13));
}
#[test]
fn test_sequential_buffer_get_after_prefetch() {
let (mut graph_file, _temp_file) = create_test_graph_file_with_nodes(10);
let mut buffer = SequentialReadBuffer::new();
buffer.prefetch_from(&mut graph_file, 1).unwrap();
let node = buffer.get(3);
assert!(node.is_some());
assert_eq!(node.unwrap().id, 3);
let node = buffer.get(99);
assert!(node.is_none());
}
#[test]
fn test_sequential_buffer_custom_prefetch_window() {
let (mut graph_file, _temp_file) = create_test_graph_file_with_nodes(20);
let mut buffer = SequentialReadBuffer::with_prefetch_window(4);
buffer.prefetch_from(&mut graph_file, 5).unwrap();
assert!(buffer.contains(5));
assert!(buffer.contains(8));
assert!(!buffer.contains(9));
assert_eq!(buffer.len(), 4);
}
#[test]
fn test_read_slots_batch_single_node() {
let (mut graph_file, _temp_file) = create_test_graph_file_with_nodes(10);
let mut node_store = NodeStore::new(&mut graph_file);
let result = node_store.read_slots_batch(5, 1);
assert!(result.is_ok());
let nodes = result.unwrap();
assert_eq!(nodes.len(), 1);
assert_eq!(nodes[0].id, 5);
assert_eq!(nodes[0].kind, "TestNode");
assert_eq!(nodes[0].name, "node_5");
}
#[test]
fn test_sequential_buffer_empty_after_prefetch_error() {
let (mut graph_file, _temp_file) = create_test_graph_file_with_nodes(5);
let mut buffer = SequentialReadBuffer::new();
let result = buffer.prefetch_from(&mut graph_file, 99);
assert!(result.is_err());
assert!(buffer.is_empty());
assert!(!buffer.contains(1));
}
}