haematite-cli 0.21.1

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

use super::{Subject, subjects};
use crate::serve::protocol::{HelloAgreement, Operation};

type TestResult = Result<(), Box<dyn std::error::Error>>;

fn operation(method: &str, params: &Value) -> Result<Operation, serde_json::Error> {
    // Through text, as the wire carries it: a document is a raw JSON value.
    serde_json::from_str(&json!({"method": method, "params": params}).to_string())
}

fn mutation(kind: &str, value: &Value) -> Result<Operation, serde_json::Error> {
    operation(
        "mutation.apply",
        &json!({
            "request_id": "r", "branch": "main",
            "expected": {"generation": "1", "sequence": "1"},
            "change": {"kind": kind, "value": value},
        }),
    )
}

/// The wire names and resource names a request asks about, in order.
fn asked(subjects: &[Subject]) -> Vec<(&str, &str, Option<&str>)> {
    subjects
        .iter()
        .map(|subject| {
            (
                subject.route.wire,
                subject.route.kind,
                subject.name.as_deref(),
            )
        })
        .collect()
}

#[test]
fn hello_asks_about_nothing() -> TestResult {
    assert!(subjects(&Operation::Hello(HelloAgreement::client()))?.is_empty());
    Ok(())
}

#[test]
fn a_document_read_asks_about_its_collection() -> TestResult {
    let read = operation(
        "documents.get",
        &json!({"selection": {"branch": "main"}, "collection": "patients", "document_id": "a"}),
    )?;
    assert_eq!(
        asked(&subjects(&read)?),
        vec![("documents.get", "collection", Some("patients"))]
    );
    Ok(())
}

#[test]
fn a_mutation_is_decided_by_its_change_kind() -> TestResult {
    let put = mutation(
        "document.put",
        &json!({"collection": "patients", "document_id": "a", "document": {}}),
    )?;
    assert_eq!(
        asked(&subjects(&put)?),
        vec![
            ("document.put", "collection", Some("patients")),
            ("mutation.apply", "branch", Some("main")),
        ]
    );
    let fork = mutation(
        "branch.fork",
        &json!({"name": "trial", "from": {"generation": "1", "sequence": "1"}}),
    )?;
    assert_eq!(
        asked(&subjects(&fork)?),
        vec![("branch.fork", "database", None)]
    );
    Ok(())
}

/// An edge acts on both its ends, so a pass that may write only one of the two
/// collections cannot link them.
#[test]
fn an_edge_asks_about_the_collections_at_both_ends() -> TestResult {
    let edge = mutation(
        "edge.put",
        &json!({
            "relation": "treats",
            "from": {"collection": "clinicians", "document_id": "c"},
            "to": {"collection": "patients", "document_id": "p"},
            "properties": {},
        }),
    )?;
    assert_eq!(
        asked(&subjects(&edge)?),
        vec![
            ("edge.put", "collection", Some("clinicians")),
            ("edge.put", "collection", Some("patients")),
            ("mutation.apply", "branch", Some("main")),
        ]
    );
    let same = mutation(
        "edge.delete",
        &json!({
            "relation": "refers",
            "from": {"collection": "patients", "document_id": "a"},
            "to": {"collection": "patients", "document_id": "b"},
        }),
    )?;
    assert_eq!(
        asked(&subjects(&same)?),
        vec![
            ("edge.delete", "collection", Some("patients")),
            ("mutation.apply", "branch", Some("main")),
        ]
    );
    Ok(())
}

#[test]
fn an_edge_import_asks_about_every_collection_it_links_once() -> TestResult {
    let edge = |from: &str, to: &str| {
        json!({
            "relation": "r",
            "from": {"collection": from, "document_id": "x"},
            "to": {"collection": to, "document_id": "y"},
            "properties": {},
        })
    };
    let import = mutation(
        "edges.put_many",
        &json!({"edges": [edge("a", "b"), edge("b", "c"), edge("a", "c")]}),
    )?;
    assert_eq!(
        asked(&subjects(&import)?),
        vec![
            ("edges.put_many", "collection", Some("a")),
            ("edges.put_many", "collection", Some("b")),
            ("edges.put_many", "collection", Some("c")),
            ("mutation.apply", "branch", Some("main")),
        ]
    );
    Ok(())
}

#[test]
fn a_branch_route_asks_about_its_branch() -> TestResult {
    let merge = operation(
        "branch.merge_prepare",
        &json!({"branch": "trial", "limit": 10}),
    )?;
    assert_eq!(
        asked(&subjects(&merge)?),
        vec![("branch.merge_prepare", "branch", Some("trial"))]
    );
    Ok(())
}

#[test]
fn a_vector_import_asks_about_every_written_collection_once() -> TestResult {
    let vector = |collection: &str, id: &str| json!({"collection":collection, "document_id":id, "field":"supplied", "vector":[1.0]});
    let operation = mutation(
        "vectors.put_many",
        &json!({"vectors":[vector("a", "x"), vector("b", "x"), vector("a", "y")]}),
    )?;
    assert_eq!(
        asked(&subjects(&operation)?),
        vec![
            ("vectors.put_many", "collection", Some("a")),
            ("vectors.put_many", "collection", Some("b")),
            ("mutation.apply", "branch", Some("main")),
        ]
    );
    Ok(())
}

#[test]
fn a_delete_import_asks_about_its_collection_and_branch() -> TestResult {
    let operation = mutation(
        "documents.delete_many",
        &json!({"collection":"patients", "document_ids":["b", "a"]}),
    )?;
    assert_eq!(
        asked(&subjects(&operation)?),
        vec![
            ("documents.delete_many", "collection", Some("patients")),
            ("mutation.apply", "branch", Some("main")),
        ]
    );
    Ok(())
}