graph_d 1.3.2

A native graph database implementation in Rust with built-in JSON support and SQLite-like simplicity
Documentation
//! Benchmark regression tests
//!
//! Validates: B2 (Target 140K+ nodes/sec creation performance)
//! Satisfies: RT-3 (Performance claims MUST be verifiable)
//! Satisfies: GAP-5 (No automated benchmark baseline in CI)
//!
//! These tests verify that performance meets minimum thresholds.
//! They are not meant to replace criterion benchmarks but to catch
//! significant regressions in CI.

use graph_d::Graph;
use serde_json::json;
use std::collections::HashMap;
use std::time::Instant;

/// Minimum acceptable node creation rate (nodes per second)
/// Based on B2 constraint: 140K+ nodes/sec target
const MIN_NODE_CREATION_RATE: f64 = 100_000.0; // Conservative threshold for CI

/// Minimum acceptable relationship creation rate
const MIN_RELATIONSHIP_CREATION_RATE: f64 = 50_000.0;

/// Minimum acceptable query throughput (queries per second)
const MIN_QUERY_THROUGHPUT: f64 = 10_000.0;

/// Validates: B2 - Node creation performance
/// Tests that we can create nodes at an acceptable rate
#[test]
fn test_node_creation_performance() {
    let mut graph = Graph::new().unwrap();
    let iterations = 10_000;

    let start = Instant::now();
    for i in 0..iterations {
        let mut props = HashMap::new();
        props.insert("id".to_string(), json!(i));
        props.insert("name".to_string(), json!(format!("node_{}", i)));
        graph.create_node(props).unwrap();
    }
    let elapsed = start.elapsed();

    let rate = iterations as f64 / elapsed.as_secs_f64();
    println!(
        "Node creation rate: {:.0} nodes/sec (minimum: {:.0})",
        rate, MIN_NODE_CREATION_RATE
    );

    assert!(
        rate >= MIN_NODE_CREATION_RATE,
        "Node creation rate {:.0}/sec is below minimum threshold {:.0}/sec. \
         This indicates a performance regression. Constraint B2 requires 140K+/sec.",
        rate,
        MIN_NODE_CREATION_RATE
    );
}

/// Validates: B2 - Relationship creation performance
#[test]
fn test_relationship_creation_performance() {
    let mut graph = Graph::new().unwrap();

    // Create nodes first
    let mut node_ids = Vec::new();
    for i in 0..1000 {
        let mut props = HashMap::new();
        props.insert("id".to_string(), json!(i));
        let id = graph.create_node(props).unwrap();
        node_ids.push(id);
    }

    let iterations = 5_000;
    let start = Instant::now();

    for i in 0..iterations {
        let from = node_ids[i % node_ids.len()];
        let to = node_ids[(i + 1) % node_ids.len()];
        let mut props = HashMap::new();
        props.insert("weight".to_string(), json!(i));
        graph
            .create_relationship(from, to, "CONNECTS".to_string(), props)
            .unwrap();
    }
    let elapsed = start.elapsed();

    let rate = iterations as f64 / elapsed.as_secs_f64();
    println!(
        "Relationship creation rate: {:.0} rels/sec (minimum: {:.0})",
        rate, MIN_RELATIONSHIP_CREATION_RATE
    );

    assert!(
        rate >= MIN_RELATIONSHIP_CREATION_RATE,
        "Relationship creation rate {:.0}/sec is below minimum threshold {:.0}/sec",
        rate,
        MIN_RELATIONSHIP_CREATION_RATE
    );
}

/// Validates: B1 - O(1) adjacency lookup performance
#[test]
fn test_adjacency_lookup_performance() {
    let mut graph = Graph::new().unwrap();

    // Create a graph with known structure
    let mut node_ids = Vec::new();
    for i in 0..1000 {
        let mut props = HashMap::new();
        props.insert("id".to_string(), json!(i));
        let id = graph.create_node(props).unwrap();
        node_ids.push(id);
    }

    // Create relationships
    for i in 0..999 {
        let props = HashMap::new();
        graph
            .create_relationship(node_ids[i], node_ids[i + 1], "NEXT".to_string(), props)
            .unwrap();
    }

    // Test lookup performance
    let iterations = 10_000;
    let start = Instant::now();

    for i in 0..iterations {
        let node_id = node_ids[i % node_ids.len()];
        let _ = graph.get_node(node_id);
    }
    let elapsed = start.elapsed();

    let rate = iterations as f64 / elapsed.as_secs_f64();
    println!(
        "Node lookup rate: {:.0} lookups/sec (minimum: {:.0})",
        rate, MIN_QUERY_THROUGHPUT
    );

    assert!(
        rate >= MIN_QUERY_THROUGHPUT,
        "Node lookup rate {:.0}/sec is below minimum threshold {:.0}/sec. \
         Constraint B1 requires O(1) adjacency lookups.",
        rate,
        MIN_QUERY_THROUGHPUT
    );
}

/// Test that performance scales linearly with data size
/// Validates: B2 performance doesn't degrade significantly with scale
#[test]
fn test_performance_scaling() {
    // Use larger sample sizes to reduce timing variance in CI environments
    let sizes = [1000, 5000, 10000];
    let mut rates = Vec::new();

    // Warmup run to ensure consistent cache state
    {
        let mut warmup_graph = Graph::new().unwrap();
        for i in 0..500 {
            let mut props = HashMap::new();
            props.insert("id".to_string(), json!(i));
            warmup_graph.create_node(props).unwrap();
        }
    }

    for &size in &sizes {
        let mut graph = Graph::new().unwrap();

        let start = Instant::now();
        for i in 0..size {
            let mut props = HashMap::new();
            props.insert("id".to_string(), json!(i));
            graph.create_node(props).unwrap();
        }
        let elapsed = start.elapsed();

        let rate = size as f64 / elapsed.as_secs_f64();
        rates.push(rate);
        println!("Size {}: {:.0} nodes/sec", size, rate);
    }

    // Performance should not degrade more than 70% at larger scales
    // Using 0.3 threshold (30% of baseline) to account for CI environment variance
    // Real-world performance is validated by separate criterion benchmarks
    let degradation = rates[2] / rates[0];
    println!("Performance ratio (10000 vs 1000): {:.2}", degradation);

    assert!(
        degradation > 0.3,
        "Performance degrades too much at scale: {:.1}% of baseline. \
         This may indicate a performance regression.",
        degradation * 100.0
    );
}