203 lines
6.8 KiB
Rust
Raw Normal View History

2025-05-21 19:23:13 -04:00
use std::collections::HashMap;
use std::collections::HashSet;
use std::sync::Arc;
use rand::random_range;
use crate::{Graph, Reference, random_object};
fn get_random_candidate(graph: &Graph) -> Arc<Reference> {
// Pick a random leaf node to update
let refs: Vec<Arc<Reference>> =graph.refs.values().filter(|r| r.name != graph.root.name).map(|r| r.clone()).collect();
let random_index = random_range(0..refs.len());
refs[random_index].clone()
}
/// Tests that all dependencies are kept updated when new nodes are added
#[test]
fn test_dependencies_updated_when_nodes_added() {
// Create a simple graph
let mut graph = create_test_graph();
// Get the initial content address of the root
let initial_root_id = graph.root.id.clone();
let candidate = get_random_candidate(&graph);
// Update the leaf node
2025-06-12 20:50:39 -04:00
graph.update_object_reference(&candidate.name, random_object().1).unwrap();
2025-05-21 19:23:13 -04:00
// Verify that the leaf node's ID has changed
2025-06-12 20:50:39 -04:00
let updated_leaf = dbg!(graph.get_reference(&candidate.name).unwrap());
2025-05-21 19:23:13 -04:00
assert_ne!(updated_leaf.id, candidate.id,
"Leaf node ID should change when content is updated");
// Verify that the root's ID has changed
assert_ne!(graph.root.id, initial_root_id,
"Root ID should change when a dependent node is updated");
}
/// Tests that the root of the graph is not itself a dependency of any other node
#[test]
fn test_root_not_a_dependency() {
let graph = create_test_graph();
let root_name = graph.root.name.clone();
// Check all references to ensure none have the root as a dependent
for (_, reference) in graph.refs.as_ref() {
for dep in &reference.dependents {
assert_ne!(dep.name, root_name,
"Root should not be a dependency of any other node");
}
}
}
/// Tests that all nodes are dependents or transitive dependents of the root
#[test]
fn test_all_nodes_connected_to_root() {
let graph = create_test_graph();
// Collect all nodes reachable from the root
let mut reachable = HashSet::new();
fn collect_reachable(node: &Arc<Reference>, reachable: &mut HashSet<String>) {
reachable.insert(node.name.clone());
for dep in &node.dependents {
if !reachable.contains(&dep.name) {
collect_reachable(dep, reachable);
}
}
}
collect_reachable(&graph.root, &mut reachable);
// Check that all nodes in the graph are reachable from the root
for (name, _) in graph.refs.as_ref() {
assert!(reachable.contains(name),
"All nodes should be reachable from the root: {}", name);
}
}
/// Helper function to create a test graph with a known structure
fn create_test_graph() -> Graph {
let root_name = String::from("/root");
let mut objects = HashMap::new();
let mut refs = HashMap::new();
// Create the root reference
let mut root_ref = Reference::new(
2025-06-12 20:50:39 -04:00
Some(String::from("root_content")),
2025-05-21 19:23:13 -04:00
root_name.clone(),
);
// Create 3 item references
for i in 1..=3 {
let item_name = format!("/item/{}", i);
let mut item_ref = Reference::new(
2025-06-12 20:50:39 -04:00
Some(format!("item_content_{}", i)),
2025-05-21 19:23:13 -04:00
item_name.clone(),
);
// Create 3 subitems for each item
for j in 1..=3 {
let (address, content) = random_object();
let subitem_name = format!("/item/{}/subitem/{}", i, j);
// Create a leaf reference
let leaf_ref = Reference::new(
2025-06-12 20:50:39 -04:00
Some(address.clone()),
2025-05-21 19:23:13 -04:00
subitem_name,
).to_arc();
// Add the leaf reference as a dependent to the item reference
item_ref = item_ref.add_dep(leaf_ref.clone());
// Store the content in the objects map
objects.insert(address.clone(), content);
// Store the leaf reference in the refs map
refs.insert(leaf_ref.name.clone(), leaf_ref);
}
// Convert the item reference to Arc and add it to the root reference
let arc_item_ref = item_ref.to_arc();
root_ref = root_ref.add_dep(arc_item_ref.clone());
// Store the item reference in the refs map
refs.insert(arc_item_ref.name.clone(), arc_item_ref);
}
// Convert the root reference to Arc
let arc_root_ref = root_ref.to_arc();
// Store the root reference in the refs map
refs.insert(arc_root_ref.name.clone(), arc_root_ref.clone());
Graph {
root: arc_root_ref,
refs: Arc::new(refs),
objects: Arc::new(objects),
}
}
/// Tests that the graph correctly handles content-addressable properties
#[test]
fn test_content_addressable_properties() {
let mut graph = create_test_graph();
// Update a leaf node with the same content
let leaf_path = "/item/1/subitem/1".to_string();
let initial_leaf = graph.get_reference(&leaf_path).unwrap();
2025-06-12 20:50:39 -04:00
if let Some(content_address) = initial_leaf.content_address.clone() {
// Get the content for this address
let content = graph.get_object(&content_address).unwrap().clone();
// Update with the same content
graph.update_object_reference(&leaf_path, content).unwrap();
}
2025-05-21 19:23:13 -04:00
// Verify that nothing changed since the content is the same
let updated_leaf = graph.get_reference(&leaf_path).unwrap();
assert_eq!(updated_leaf.content_address, initial_leaf.content_address,
"Content address should not change when content remains the same");
}
/// Tests that the graph correctly handles ID calculation
#[test]
fn test_id_calculation() {
let mut graph = create_test_graph();
// Update a leaf node
let leaf_path = "/item/1/subitem/1".to_string();
let initial_leaf = graph.get_reference(&leaf_path).unwrap();
2025-06-12 20:50:39 -04:00
graph.update_object_reference(&leaf_path, "new content".as_bytes().to_vec()).unwrap();
2025-05-21 19:23:13 -04:00
// Verify that the ID changed
let updated_leaf = graph.get_reference(&leaf_path).unwrap();
assert_ne!(updated_leaf.id, initial_leaf.id,
"Reference ID should change when content changes");
// Verify that parent ID changed
let parent_path = "/item/1".to_string();
let parent = graph.get_reference(&parent_path).unwrap();
// Create a reference with the same properties to calculate expected ID
let mut test_ref = Reference::new(
parent.content_address.clone(),
parent.name.clone(),
);
// Add the same dependents
for dep in &parent.dependents {
test_ref = test_ref.add_dep(dep.clone());
}
// Verify the ID calculation is consistent
assert_eq!(parent.id, test_ref.id,
"ID calculation should be consistent for the same reference properties");
}