use sqlitegraph::{EdgeSpec, GraphConfig, NodeSpec, open_graph};
use std::fs;
use std::io::Read;
fn read_node_slot_version(
path: &std::path::Path,
node_id: i64,
) -> Result<u8, Box<dyn std::error::Error>> {
let mut file = fs::File::open(path)?;
const NODE_SLOT_SIZE: u64 = 4096;
const DEFAULT_NODE_DATA_START: u64 = 1024;
let slot_offset = DEFAULT_NODE_DATA_START + ((node_id - 1) as u64 * NODE_SLOT_SIZE);
use std::io::Seek;
use std::io::SeekFrom;
file.seek(SeekFrom::Start(slot_offset))?;
let mut buffer = [0u8; 1];
file.read_exact(&mut buffer)?;
Ok(buffer[0])
}
fn print_node_status(db_path: &std::path::Path, node_ids: &[i64], label: &str) {
println!("=== {} ===", label);
for &node_id in node_ids {
match read_node_slot_version(db_path, node_id) {
Ok(version) => {
let slot_offset = 1024 + ((node_id - 1) as u64 * 4096);
println!(
"Node {}: slot_offset=0x{:x}, version={}",
node_id, slot_offset, version
);
}
Err(e) => {
println!("Node {}: ERROR reading slot - {}", node_id, e);
}
}
}
}
#[test]
fn test_edge_insertion_corruption_isolation() {
println!("=== EDGE INSERTION CORRUPTION ISOLATION TEST ===");
let temp_dir = tempfile::tempdir().unwrap();
let db_path = temp_dir.path().join("test.db");
let graph =
open_graph(&db_path, &GraphConfig::native()).expect("Failed to create V2 native graph");
let mut node_ids = Vec::new();
for i in 1..=300 {
let node_id = graph
.insert_node(NodeSpec {
kind: "Node".to_string(),
name: format!("node_{}", i),
file_path: None,
data: serde_json::json!({"id": i}),
})
.expect(&format!("Failed to insert node {}", i));
node_ids.push(node_id);
}
let critical_nodes = [255, 256, 257, 258, 259, 260];
print_node_status(&db_path, &critical_nodes, "AFTER NODE CREATION");
for &node_id in &critical_nodes {
let version = read_node_slot_version(&db_path, node_id).unwrap();
assert_eq!(
version, 2,
"Node {} should have version=2 after node creation",
node_id
);
}
println!("\n=== BEGINNING EDGE INSERTION WITH MONITORING ===");
for i in 0..299 {
let from_id = node_ids[i];
let to_id = node_ids[i + 1];
if (from_id >= 250 && from_id <= 270) || (to_id >= 250 && to_id <= 270) {
println!("\n--- EDGE {}: {} -> {} ---", i, from_id, to_id);
for &node_id in &critical_nodes {
let version = read_node_slot_version(&db_path, node_id).unwrap();
if version != 2 {
println!(
"CORRUPTION DETECTED BEFORE EDGE {} - Node {} has version {}",
i, node_id, version
);
panic!(
"Node {} already corrupted before inserting edge {} ({} -> {})",
node_id, i, from_id, to_id
);
}
}
}
match graph.insert_edge(EdgeSpec {
from: from_id,
to: to_id,
edge_type: "chain".to_string(),
data: serde_json::json!({"edge_index": i}),
}) {
Ok(_) => {
if (from_id >= 250 && from_id <= 270) || (to_id >= 250 && to_id <= 270) {
print_node_status(
&db_path,
&critical_nodes,
&format!("AFTER EDGE {}: {} -> {}", i, from_id, to_id),
);
for &node_id in &critical_nodes {
let version = read_node_slot_version(&db_path, node_id).unwrap();
if version != 2 {
println!(
"🔥 CORRUPTION DETECTED! Node {} has version {} after inserting edge {} ({} -> {})",
node_id, version, i, from_id, to_id
);
panic!(
"CORRUPTION CONFIRMED: Edge {} ({} -> {}) corrupted node {} to version {}",
i, from_id, to_id, node_id, version
);
}
}
}
}
Err(e) => {
println!("Edge {} ({} -> {}) FAILED: {}", i, from_id, to_id, e);
if e.to_string().contains("uninitialized slot")
|| e.to_string().contains("version=0")
{
println!(
"🔥 TARGET CORRUPTION DETECTED at edge {} ({} -> {})",
i, from_id, to_id
);
print_node_status(&db_path, &critical_nodes, "CORRUPTION DETECTED");
panic!(
"REPRODUCED: V2 uninitialized slot corruption detected at edge {}",
i
);
} else {
panic!("Unexpected error (not uninitialized slot): {}", e);
}
}
}
}
println!("\n=== EDGE INSERTION COMPLETED SUCCESSFULLY ===");
print_node_status(&db_path, &critical_nodes, "FINAL VERIFICATION");
for &node_id in &critical_nodes {
let version = read_node_slot_version(&db_path, node_id).unwrap();
assert_eq!(
version, 2,
"Node {} should still have version=2 after all edge insertions",
node_id
);
}
println!("✅ NO CORRUPTION DETECTED - All edge insertions completed successfully");
}
#[test]
fn test_direct_edge_to_node_257() {
println!("=== DIRECT EDGE TO NODE 257 TEST ===");
let temp_dir = tempfile::tempdir().unwrap();
let db_path = temp_dir.path().join("test.db");
let graph =
open_graph(&db_path, &GraphConfig::native()).expect("Failed to create V2 native graph");
let mut node_ids = Vec::new();
for i in 1..=257 {
let node_id = graph
.insert_node(NodeSpec {
kind: "Node".to_string(),
name: format!("node_{}", i),
file_path: None,
data: serde_json::json!({"id": i}),
})
.expect(&format!("Failed to insert node {}", i));
node_ids.push(node_id);
}
print_node_status(&db_path, &[255, 256, 257], "AFTER NODE CREATION");
let version_257 = read_node_slot_version(&db_path, 257).unwrap();
assert_eq!(
version_257, 2,
"Node 257 should have version=2 after creation"
);
println!("Inserting edge: 256 -> 257");
match graph.insert_edge(EdgeSpec {
from: node_ids[255], to: node_ids[256], edge_type: "test".to_string(),
data: serde_json::json!({"test": true}),
}) {
Ok(_) => {
println!("Edge 256 -> 257 inserted successfully");
let version_257_after = read_node_slot_version(&db_path, 257).unwrap();
println!(
"Node 257 version after edge insertion: {}",
version_257_after
);
assert_eq!(
version_257_after, 2,
"Node 257 should still have version=2 after edge insertion"
);
}
Err(e) => {
println!("Edge 256 -> 257 failed: {}", e);
if e.to_string().contains("uninitialized slot") || e.to_string().contains("version=0") {
panic!("REPRODUCED: Node 257 corruption when inserting edge 256 -> 257");
} else {
panic!("Different error occurred: {}", e);
}
}
}
println!("Direct edge to node 257 test completed successfully");
}