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])
}
#[test]
fn test_prove_transaction_begin_does_not_corrupt_node_slots() {
println!("=== PROVING TRANSACTION-BEGIN DOES NOT CORRUPT NODE SLOTS ===");
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];
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 creation",
node_id
);
}
println!("✅ All 300 nodes created successfully with version=2");
let version_257_before = read_node_slot_version(&db_path, 257).unwrap();
println!(
"Node 257 version before edge insertion: {}",
version_257_before
);
assert_eq!(
version_257_before, 2,
"Node 257 should have version=2 before edge insertion"
);
println!("Inserting edge: 1 -> 2 (this calls begin_transaction internally)");
match graph.insert_edge(EdgeSpec {
from: node_ids[0], to: node_ids[1], edge_type: "test".to_string(),
data: serde_json::json!({"test": true}),
}) {
Ok(_) => println!("✅ Edge inserted successfully"),
Err(e) => {
println!("Edge insertion failed: {}", e);
if e.to_string().contains("uninitialized slot") || e.to_string().contains("version=0") {
println!("🔥 CORRUPTION DETECTED during edge insertion!");
for &node_id in &critical_nodes {
let version = read_node_slot_version(&db_path, node_id).unwrap();
if version != 2 {
println!("CORRUPTED NODE: {} has version {}", node_id, version);
}
}
panic!("Node slot corruption detected during edge insertion");
} else {
panic!("Different error during edge insertion: {}", e);
}
}
}
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"
);
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 edge insertion",
node_id
);
}
println!("✅ PROVEN: Transaction begin does NOT corrupt node slots");
println!("✅ All critical nodes maintain version=2 throughout transaction begin");
}
#[test]
fn test_multiple_transaction_begin_operations() {
println!("=== TESTING MULTIPLE TRANSACTION BEGIN OPERATIONS ===");
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 250..=270 {
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);
}
for i in 250..=270 {
let version = read_node_slot_version(&db_path, i).unwrap();
assert_eq!(
version, 2,
"Node {} should have version=2 after creation",
i
);
}
println!("Inserting multiple edges, each calling begin_transaction...");
for i in 0..10 {
let from_idx = i;
let to_idx = i + 1;
match graph.insert_edge(EdgeSpec {
from: node_ids[from_idx],
to: node_ids[to_idx],
edge_type: "test".to_string(),
data: serde_json::json!({"edge_index": i}),
}) {
Ok(_) => {
let check_nodes = [255, 256, 257, 258, 259];
for &node_id in &check_nodes {
if node_id >= 250 && node_id <= 270 {
let version = read_node_slot_version(&db_path, node_id).unwrap();
assert_eq!(
version,
2,
"Node {} corrupted to version {} after edge {} ({} -> {})",
node_id,
version,
i,
250 + from_idx,
250 + to_idx
);
}
}
println!("Edge {} ({} -> {}): OK", i, 250 + from_idx, 250 + to_idx);
}
Err(e) => {
if e.to_string().contains("uninitialized slot")
|| e.to_string().contains("version=0")
{
panic!(
"CORRUPTION DETECTED at edge {} ({} -> {})",
i,
250 + from_idx,
250 + to_idx
);
} else {
panic!("Different error at edge {}: {}", i, e);
}
}
}
}
println!("✅ Multiple transaction begin operations completed without corruption");
}