iridium-db 0.4.0

A high-performance vector-graph hybrid storage and indexing engine
use super::*;

fn open(base: &std::path::Path) -> StorageHandle {
    open_store(StorageConfig {
        buffer_pool_pages: 8,
        wal_dir: base.join("wal"),
        wal_segment_max_bytes: 1 << 20,
        manifest_path: base.join("ir.manifest"),
        sstable_dir: base.join("sst"),
    })
    .unwrap()
}

/// QuantizedI8 vector: encode → put_vector_delta → stored payload decodes back correctly.
#[test]
fn quantized_i8_encode_put_decode_round_trip() {
    let base = temp_dir("qi8_storage_round_trip");
    let mut handle = open(&base);

    let original = vec![0.8_f32, -0.4, 0.2, 0.1];
    let space_id = 5_u32;
    let node_id = 42_u64;

    put_full_node(&mut handle, node_id, 1, &[]).unwrap();

    let payload =
        encode_vector_payload_quantized_i8(space_id, VectorMetric::Cosine, &original, false)
            .unwrap();
    let delta = encode_delta(node_id, 2, &payload);
    put_vector_delta(&mut handle, &delta).unwrap();

    // Retrieve and decode the stored vector delta.
    let logical = get_logical_node(&mut handle, node_id).unwrap();
    let vector_delta = logical
        .deltas
        .iter()
        .find(|e| e.kind == crate::features::storage::sstable::EntryKind::VectorDelta)
        .expect("vector delta must be present");

    let decoded = decode_vector_payload(&vector_delta.value).unwrap();
    let DecodedVectorPayload::Structured(vec) = decoded else {
        panic!("expected structured vector payload");
    };
    assert_eq!(vec.descriptor.space_id, space_id);
    assert_eq!(vec.descriptor.encoding, VectorEncoding::QuantizedI8);
    assert_eq!(vec.descriptor.metric, VectorMetric::Cosine);
    assert_eq!(vec.values.len(), original.len());
    for (got, expected) in vec.values.iter().zip(original.iter()) {
        assert!(
            (got - expected).abs() < 0.02,
            "quantized round-trip error too large: got {got}, expected {expected}"
        );
    }
}

/// QuantizedI8 cosine vector is inserted into HNSW and searchable.
#[test]
fn quantized_i8_cosine_vector_inserted_into_hnsw() {
    let base = temp_dir("qi8_hnsw_insert");
    let mut handle = open(&base);

    let space_id = 7_u32;
    put_full_node(&mut handle, 1, 1, &[]).unwrap();
    put_full_node(&mut handle, 2, 1, &[]).unwrap();

    let payload1 =
        encode_vector_payload_quantized_i8(space_id, VectorMetric::Cosine, &[1.0, 0.0], false)
            .unwrap();
    let payload2 =
        encode_vector_payload_quantized_i8(space_id, VectorMetric::Cosine, &[0.0, 1.0], false)
            .unwrap();
    put_vector_delta(&mut handle, &encode_delta(1, 2, &payload1)).unwrap();
    put_vector_delta(&mut handle, &encode_delta(2, 2, &payload2)).unwrap();

    let results = hnsw_search_in_space(&handle, space_id, &[1.0, 0.0], 5);
    assert!(
        !results.is_empty(),
        "HNSW search should return results for QuantizedI8 cosine space"
    );
    let top_id = results[0].0;
    assert_eq!(top_id, 1, "node 1 ([1,0]) should be nearest to query [1,0]");
}

/// QuantizedI8 vector survives WAL replay with correct values.
#[test]
fn quantized_i8_wal_replay_round_trip() {
    let base = temp_dir("qi8_wal_replay");
    let original = vec![0.6_f32, -0.8];
    let space_id = 9_u32;
    let node_id = 55_u64;

    {
        let mut handle = open(&base);
        put_full_node(&mut handle, node_id, 1, &[]).unwrap();
        let payload =
            encode_vector_payload_quantized_i8(space_id, VectorMetric::Cosine, &original, false)
                .unwrap();
        put_vector_delta(&mut handle, &encode_delta(node_id, 2, &payload)).unwrap();
    }

    let mut handle = open(&base);
    recover_from_wal(&mut handle).unwrap();

    let logical = get_logical_node(&mut handle, node_id).unwrap();
    let vector_delta = logical
        .deltas
        .iter()
        .find(|e| e.kind == crate::features::storage::sstable::EntryKind::VectorDelta)
        .expect("vector delta must survive WAL replay");

    let decoded = decode_vector_payload(&vector_delta.value).unwrap();
    let DecodedVectorPayload::Structured(vec) = decoded else {
        panic!("expected structured vector payload after WAL replay");
    };
    assert_eq!(vec.descriptor.encoding, VectorEncoding::QuantizedI8);
    assert_eq!(vec.values.len(), original.len());
    for (got, expected) in vec.values.iter().zip(original.iter()) {
        assert!(
            (got - expected).abs() < 0.02,
            "round-trip error after WAL replay too large: got {got}, expected {expected}"
        );
    }
}