haematite-cli 0.21.8

The haematite command: serve a store, its door and Studio, answer MCP, query with jq, and host the store's assistant seat
Documentation
use haematite::SyncCounts;
use serde_json::json;

use super::batch_test_support::{ONE_WRITE, TestResult, apply, at, record_id, store};
use super::{Storage, reads};
use crate::serve::protocol::{Cardinality, Change, Edge, EdgeImport, Node, Relation};

fn node(id: &str) -> Node {
    Node {
        collection: "records".to_owned(),
        document_id: id.to_owned(),
    }
}

fn edge(index: usize) -> Edge {
    Edge {
        relation: "links".to_owned(),
        from: node(&record_id(index)),
        to: node("anchor"),
        properties: json!({"index":index}),
    }
}

fn declare(storage: &mut Storage) -> TestResult {
    apply(
        storage,
        "relation",
        Change::RelationPut(Relation {
            relation: "links".to_owned(),
            from_collection: "records".to_owned(),
            to_collection: "records".to_owned(),
            cardinality: Cardinality::ManyToMany,
        }),
    )?;
    Ok(())
}

#[test]
fn five_hundred_edges_share_one_commit_and_the_control_flushes() -> TestResult {
    let (_directory, mut storage) = store(500)?;
    declare(&mut storage)?;
    let before = at(&storage)?.version;
    let counts = storage.sync_counts();
    let receipt = apply(
        &mut storage,
        "edges",
        Change::EdgePutMany(EdgeImport {
            edges: (0..500).map(edge).collect(),
        }),
    )?;
    assert_eq!(receipt.version.sequence, before.sequence + 1);
    assert_eq!(storage.sync_counts().since(counts), ONE_WRITE);
    let captured = at(&storage)?;
    for prefix in [b"e\0".as_slice(), b"i\0".as_slice()] {
        assert_eq!(
            reads::test_support::count_keys(&storage.reader, &captured, prefix, 5)?,
            500,
        );
    }
    let access = crate::serve::access::request(&crate::serve::access::Standalone)?;
    let audit = serde_json::to_value(Storage::audit_counts(&access, &storage.reader, &captured)?)?;
    assert!(
        audit["buckets"]
            .as_array()
            .ok_or("no count buckets")?
            .iter()
            .all(|row| row["result"] == "agree")
    );
    Ok(())
}

#[test]
fn missing_endpoint_at_137_refuses_five_hundred_edges_without_flushes() -> TestResult {
    let (_directory, mut storage) = store(500)?;
    declare(&mut storage)?;
    let before = at(&storage)?.version;
    let counts = storage.sync_counts();
    let mut edges: Vec<_> = (0..500).map(edge).collect();
    edges[137].to = node("unknown");
    let failure = apply(
        &mut storage,
        "edges",
        Change::EdgePutMany(EdgeImport { edges }),
    )
    .err()
    .ok_or("missing endpoint accepted")?;
    assert_eq!(failure.code, "document_missing");
    assert_eq!(failure.path.as_deref(), Some("/edges/137/to"));
    assert_eq!(at(&storage)?.version, before);
    assert_eq!(storage.sync_counts().since(counts), SyncCounts::default());
    let captured = at(&storage)?;
    for prefix in [b"e\0".as_slice(), b"i\0".as_slice()] {
        assert_eq!(
            reads::test_support::count_keys(&storage.reader, &captured, prefix, 5)?,
            0,
        );
    }
    Ok(())
}