ruvector-mincut 2.0.6

World's first subpolynomial dynamic min-cut: self-healing networks, AI optimization, real-time graph analysis
Documentation
//! Comprehensive coverage tests for ruvector-mincut
//!
//! Ensures 100% test coverage across all modules.

use ruvector_mincut::certificate::{AuditData, AuditEntryType, AuditLogger, CutCertificate};
use ruvector_mincut::connectivity::DynamicConnectivity;
use ruvector_mincut::instance::{InstanceResult, StubInstance, WitnessHandle};
use ruvector_mincut::prelude::*;
use ruvector_mincut::wrapper::MinCutWrapper;
use std::sync::Arc;

// ============================================================================
// Connectivity Tests
// ============================================================================

#[test]
fn test_connectivity_edge_cases() {
    let mut dc = DynamicConnectivity::new();

    // Empty graph
    assert!(!dc.is_connected());
    assert_eq!(dc.component_count(), 0);

    // Single vertex
    dc.add_vertex(0);
    assert!(dc.is_connected()); // Single vertex is connected
    assert_eq!(dc.component_count(), 1);

    // Two isolated vertices
    dc.add_vertex(1);
    assert!(!dc.is_connected());
    assert_eq!(dc.component_count(), 2);
}

#[test]
fn test_connectivity_path_compression() {
    let mut dc = DynamicConnectivity::new();

    // Build long chain: 0-1-2-3-4-5
    for i in 0..5 {
        dc.insert_edge(i, i + 1);
    }

    // Query should trigger path compression
    assert!(dc.connected(0, 5));
    assert!(dc.connected(0, 3));
    assert!(dc.connected(2, 4));
}

#[test]
fn test_connectivity_multiple_components() {
    let mut dc = DynamicConnectivity::new();

    // Component 1: 0-1-2
    dc.insert_edge(0, 1);
    dc.insert_edge(1, 2);

    // Component 2: 3-4
    dc.insert_edge(3, 4);

    // Component 3: 5 alone
    dc.add_vertex(5);

    assert_eq!(dc.component_count(), 3);
    assert!(dc.connected(0, 2));
    assert!(dc.connected(3, 4));
    assert!(!dc.connected(0, 3));
    assert!(!dc.connected(0, 5));
}

// ============================================================================
// Witness Tests
// ============================================================================

#[test]
fn test_witness_large_vertex_ids() {
    use roaring::RoaringBitmap;

    // Test with vertex IDs near u32::MAX limit
    let mut membership = RoaringBitmap::new();
    membership.insert(u32::MAX - 1);
    membership.insert(u32::MAX);

    let witness = WitnessHandle::new((u32::MAX - 1) as u64, membership, 5);

    assert!(witness.contains((u32::MAX - 1) as u64));
    assert!(witness.contains(u32::MAX as u64));
    assert!(!witness.contains(0));
}

#[test]
fn test_witness_equality() {
    use roaring::RoaringBitmap;

    let mut m1 = RoaringBitmap::new();
    m1.insert(1);
    m1.insert(2);

    let mut m2 = RoaringBitmap::new();
    m2.insert(1);
    m2.insert(2);

    let w1 = WitnessHandle::new(1, m1, 5);
    let w2 = WitnessHandle::new(1, m2, 5);

    assert_eq!(w1, w2);
}

#[test]
fn test_witness_materialize() {
    use roaring::RoaringBitmap;

    let mut membership = RoaringBitmap::new();
    membership.insert(1);
    membership.insert(3);
    membership.insert(5);

    let witness = WitnessHandle::new(1, membership, 3);
    let (u, v_minus_u) = witness.materialize_partition();

    assert!(u.contains(&1));
    assert!(u.contains(&3));
    assert!(u.contains(&5));
    assert_eq!(u.len(), 3);

    // v_minus_u contains 0, 2, 4 (up to max which is 5)
    assert!(v_minus_u.contains(&0));
    assert!(v_minus_u.contains(&2));
    assert!(v_minus_u.contains(&4));
}

// ============================================================================
// Instance Tests
// ============================================================================

#[test]
fn test_stub_instance_range_behavior() {
    let graph = DynamicGraph::new();
    graph.insert_edge(0, 1, 1.0).unwrap();
    graph.insert_edge(1, 2, 1.0).unwrap();

    // Range [0, 0] - min cut 1 is above range
    let mut instance = StubInstance::new(&graph, 0, 0);
    assert!(matches!(instance.query(), InstanceResult::AboveRange));

    // Range [0, 1] - min cut 1 is in range
    let mut instance = StubInstance::new(&graph, 0, 1);
    match instance.query() {
        InstanceResult::ValueInRange { value, .. } => assert_eq!(value, 1),
        _ => panic!("Expected ValueInRange"),
    }

    // Range [0, 10] - min cut 1 is in range
    let mut instance = StubInstance::new(&graph, 0, 10);
    match instance.query() {
        InstanceResult::ValueInRange { value, .. } => assert_eq!(value, 1),
        _ => panic!("Expected ValueInRange"),
    }
}

// ============================================================================
// Wrapper Tests
// ============================================================================

#[test]
fn test_wrapper_time_tracking() {
    let graph = Arc::new(DynamicGraph::new());
    let mut wrapper = MinCutWrapper::new(graph);

    assert_eq!(wrapper.current_time(), 0);

    wrapper.insert_edge(0, 1, 2);
    assert_eq!(wrapper.current_time(), 1);

    wrapper.insert_edge(1, 3, 4);
    assert_eq!(wrapper.current_time(), 2);

    wrapper.delete_edge(0, 1, 2);
    assert_eq!(wrapper.current_time(), 3);
}

// ============================================================================
// Certificate Tests
// ============================================================================

#[test]
fn test_certificate_json_roundtrip() {
    let cert = CutCertificate::new();
    let json = cert.to_json().expect("Failed to serialize certificate");

    // Verify JSON is valid and contains expected fields
    assert!(json.contains("\"witness_summaries\""));
    assert!(json.contains("\"version\""));
    assert!(json.contains("\"localkcut_responses\""));
    assert!(json.contains("\"timestamp\""));
}

#[test]
fn test_audit_logger_capacity() {
    let logger = AuditLogger::new(5);

    // Add more than capacity
    for i in 0..10 {
        logger.log(
            AuditEntryType::WitnessCreated,
            AuditData::Witness {
                hash: i,
                boundary: i,
                seed: i,
            },
        );
    }

    // Should only have last 5
    let entries = logger.export();
    assert_eq!(entries.len(), 5);
}

#[test]
fn test_audit_logger_filtering() {
    let logger = AuditLogger::new(100);

    logger.log(
        AuditEntryType::WitnessCreated,
        AuditData::Witness {
            hash: 1,
            boundary: 1,
            seed: 1,
        },
    );
    logger.log(
        AuditEntryType::LocalKCutQuery,
        AuditData::Query {
            budget: 5,
            radius: 10,
            seeds: vec![1],
        },
    );
    logger.log(
        AuditEntryType::WitnessCreated,
        AuditData::Witness {
            hash: 2,
            boundary: 2,
            seed: 2,
        },
    );

    let recent = logger.recent(2);
    assert_eq!(recent.len(), 2);
}

// ============================================================================
// Integration Tests
// ============================================================================

#[test]
fn test_full_workflow_path_to_cycle() {
    let graph = Arc::new(DynamicGraph::new());
    let mut wrapper = MinCutWrapper::new(Arc::clone(&graph));

    // Build path: 0-1-2-3
    for i in 0..3 {
        let edge_id = graph.insert_edge(i, i + 1, 1.0).unwrap();
        wrapper.insert_edge(edge_id, i, i + 1);
    }

    let result = wrapper.query();
    assert_eq!(result.value(), 1); // Path has min cut 1

    // Add edge to form cycle
    let edge_id = graph.insert_edge(3, 0, 1.0).unwrap();
    wrapper.insert_edge(edge_id, 3, 0);

    let result = wrapper.query();
    assert_eq!(result.value(), 2); // Cycle has min cut 2
}

#[test]
fn test_full_workflow_split_and_merge() {
    let graph = Arc::new(DynamicGraph::new());
    let mut wrapper = MinCutWrapper::new(Arc::clone(&graph));

    // Build triangle
    let e1 = graph.insert_edge(0, 1, 1.0).unwrap();
    let e2 = graph.insert_edge(1, 2, 1.0).unwrap();
    let e3 = graph.insert_edge(2, 0, 1.0).unwrap();

    wrapper.insert_edge(e1, 0, 1);
    wrapper.insert_edge(e2, 1, 2);
    wrapper.insert_edge(e3, 2, 0);

    assert_eq!(wrapper.query().value(), 2);

    // Delete edge to break cycle
    graph.delete_edge(2, 0).unwrap();
    wrapper.delete_edge(e3, 2, 0);

    assert_eq!(wrapper.query().value(), 1); // Now a path
}

// ============================================================================
// Edge Cases
// ============================================================================

#[test]
fn test_self_loop_ignored() {
    let graph = Arc::new(DynamicGraph::new());

    // Self-loops should be ignored or handled gracefully
    let _result = graph.insert_edge(0, 0, 1.0);
    // Implementation may accept or reject - just ensure no panic
}

#[test]
fn test_parallel_edges() {
    let graph = Arc::new(DynamicGraph::new());

    graph.insert_edge(0, 1, 1.0).unwrap();

    // Second edge between same vertices
    let _result = graph.insert_edge(0, 1, 1.0);

    // Should either fail (EdgeExists) or succeed
    // Just ensure consistent behavior
}

#[test]
fn test_large_vertex_ids() {
    let graph = Arc::new(DynamicGraph::new());

    let large_id = 1_000_000u64;
    graph.insert_edge(large_id, large_id + 1, 1.0).unwrap();

    let mut wrapper = MinCutWrapper::new(Arc::clone(&graph));
    wrapper.insert_edge(0, large_id, large_id + 1);

    let result = wrapper.query();
    assert!(result.is_connected());
}