zeppelin-embed 0.8.0

In-process vector, lexical, and hybrid search for macOS and Apple silicon
Documentation
use super::recovery::{
    commit_tail_test_node, disable_generation_fixture_maintenance, native_options, observe_node,
};
use super::tempfile;
use crate::lifecycle::{OpenOptions, Store};
use std::collections::BTreeMap;
use std::path::Path;

fn image(path: &Path) -> BTreeMap<std::ffi::OsString, Vec<u8>> {
    std::fs::read_dir(path)
        .unwrap()
        .map(|entry| {
            let entry = entry.unwrap();
            (entry.file_name(), std::fs::read(entry.path()).unwrap())
        })
        .collect()
}

#[test]
fn a_zgraph_beside_a_v2_manifest_is_unlinked_only_by_a_writable_open() {
    let directory = tempfile::tempdir().unwrap();
    let store = Store::open(directory.path(), native_options()).unwrap();
    store.close().unwrap();
    let before = image(directory.path());
    let orphan = directory
        .path()
        .join("graph-00000000000000000000000000000001.zgraph");
    std::fs::write(&orphan, b"interrupted enable").unwrap();
    let planted = image(directory.path());
    Store::open(directory.path(), OpenOptions::read_only())
        .unwrap()
        .close()
        .unwrap();
    assert_eq!(image(directory.path()), planted);
    Store::open(directory.path(), native_options())
        .unwrap()
        .close()
        .unwrap();
    assert!(
        !orphan.exists(),
        "v2 manifest has no reachable graph objects"
    );
    assert_eq!(
        image(directory.path()),
        before,
        "graph-free manifest and WAL bytes must stay unchanged"
    );
}

#[test]
fn the_legacy_sweep_never_touches_zgraph_when_graph_is_enabled() {
    let directory = tempfile::tempdir().unwrap();
    let store = Store::open(directory.path(), native_options()).unwrap();
    store.enable_graph().unwrap();
    store.close().unwrap();
    let orphan = directory
        .path()
        .join("graph-00000000000000000000000000000001.zgraph");
    std::fs::write(&orphan, b"unknown graph object").unwrap();
    let before = image(directory.path());
    Store::open(directory.path(), native_options())
        .unwrap()
        .close()
        .unwrap();
    assert_eq!(image(directory.path()), before);
}

#[test]
fn an_object_named_by_an_unabsorbed_record_is_protected() {
    let directory = tempfile::tempdir().unwrap();
    let store = Store::open(directory.path(), native_options()).unwrap();
    store.enable_graph().unwrap();
    disable_generation_fixture_maintenance(&store);
    let node = commit_tail_test_node(&store, "unabsorbed");
    store.close().unwrap();
    let before = image(directory.path());
    let manifest =
        crate::manifest::decode_manifest("test", &before[std::ffi::OsStr::new("manifest.ze")])
            .unwrap();
    assert_eq!(manifest.graph.unwrap().graph_absorbed_through, 0);
    let objects: BTreeMap<_, _> = before
        .iter()
        .filter(|(name, _)| name.to_str().unwrap().ends_with(".zgraph"))
        .map(|(name, bytes)| (name.clone(), bytes.clone()))
        .collect();
    assert!(!objects.is_empty());
    for options in [OpenOptions::read_only(), native_options()] {
        let reopened = Store::open(directory.path(), options).unwrap();
        assert!(observe_node(&reopened, node).is_some());
        for (name, bytes) in &objects {
            assert_eq!(&std::fs::read(directory.path().join(name)).unwrap(), bytes);
        }
        reopened.close().unwrap();
    }
}

#[test]
fn sweep_faults_refuse_and_resume_without_poisoning_a_list_refusal() {
    sweep_faults(1);
}

pub(super) fn sweep_faults(payload: u8) {
    use super::publication::{FaultPoint, RecordingVfs};
    use std::sync::Arc;
    for (point, skip) in [
        (FaultPoint::List, 0),
        (FaultPoint::Delete, 0),
        (FaultPoint::Delete, 1),
        (FaultPoint::DirectorySync, 0),
    ] {
        let directory = tempfile::tempdir().unwrap();
        let vfs = Arc::new(RecordingVfs::default());
        let store = Store::open_with_infrastructure(
            directory.path(),
            native_options(),
            vfs.clone(),
            Arc::new(crate::lifecycle::SystemMonotonicClock),
            None,
            None,
            None,
            None,
            None,
            None,
            None,
            None,
        )
        .unwrap();
        for id in [1, 2] {
            std::fs::write(
                directory.path().join(format!("graph-{id:032x}.zgraph")),
                [payload],
            )
            .unwrap();
        }
        let before = image(directory.path());
        vfs.arm_fault_after(point, skip);
        assert!(
            crate::lifecycle::cleanup_open_orphans(&store).is_err(),
            "{point:?} must fail the sweep"
        );
        vfs.assert_fired_once();
        if point == FaultPoint::List {
            assert_eq!(image(directory.path()), before);
            store
                .enable_graph()
                .expect("a list refusal writes nothing and must leave the writer usable");
        } else {
            assert!(
                matches!(
                    store.enable_graph(),
                    Err(crate::lifecycle::StoreError::WalWrite(
                        crate::wal::WalWriteError::Failed { .. }
                    ))
                ),
                "unlink/sync failure must fence the shared writer"
            );
        }
        drop(store);
        let reopened = Store::open(directory.path(), native_options()).unwrap();
        if point != FaultPoint::List {
            assert!(
                !directory
                    .path()
                    .join(format!("graph-{:032x}.zgraph", 1))
                    .exists()
            );
            assert!(
                !directory
                    .path()
                    .join(format!("graph-{:032x}.zgraph", 2))
                    .exists()
            );
        }
        reopened.close().unwrap();
    }
}