haematite-cli 0.21.7

The haematite command: serve a store, its door and Studio, answer MCP, query with jq, and host the store's assistant seat
Documentation
#![cfg(unix)]
#![forbid(unsafe_code)]

use serde_json::json;
use std::error::Error;
use std::process::{Command, Stdio};

#[path = "support/stand_in.rs"]
mod support;
use support::{answer, stand_in};

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

use haematite_cli::serve::protocol::NOT_APPLIED_REFUSALS as NOT_APPLIED;

#[test]
fn every_not_applied_refusal_exits_two_without_receipt_reconciliation() -> TestResult {
    let mut failures = Vec::new();
    for code in NOT_APPLIED {
        let dir = tempfile::tempdir()?;
        let refusal =
            json!({"id":"haem-call","error":{"code":code,"message":"fixture refusal"}}).to_string();
        let service = stand_in(dir.path(), 65_536, vec![vec![Some(refusal)]])?;
        let output = Command::new(env!("CARGO_BIN_EXE_haematite"))
            .args(["write", "--socket"])
            .arg(dir.path().join("serve.sock"))
            .args([
                "--timeout-ms",
                "1000",
                "--branch",
                "main",
                "--expected",
                "1.0",
                "--request-id",
                "refusal-contract",
                "collection.put",
                r#"{"collection":"records","schema":null}"#,
            ])
            .output()?;
        let frames = service.frames()?;
        assert_eq!(frames.len(), 1, "unexpected request count for {code}");
        let request: serde_json::Value =
            serde_json::from_slice(frames[0].as_deref().ok_or("no mutation request")?)?;
        assert_eq!(request["operation"]["method"], "mutation.apply");
        let stderr = std::str::from_utf8(&output.stderr)?;
        if output.status.code() != Some(2)
            || !stderr.contains(&format!("refused {code}:"))
            || !output.stdout.is_empty()
        {
            failures.push(format!(
                "{code}: exit {:?}, stderr={stderr}",
                output.status.code()
            ));
        }
    }
    assert!(
        failures.is_empty(),
        "{} of {} not-applied codes were misclassified:\n{}",
        failures.len(),
        NOT_APPLIED.len(),
        failures.join("\n")
    );
    Ok(())
}

#[test]
fn answered_but_unwritable_stdout_names_the_output_failure() -> TestResult {
    let dir = tempfile::tempdir()?;
    let service = stand_in(
        dir.path(),
        65_536,
        vec![vec![Some(answer(r#"{"applied":true}"#))]],
    )?;
    let (destination, closed_reader) = std::os::unix::net::UnixStream::pair()?;
    drop(closed_reader);
    let output = Command::new(env!("CARGO_BIN_EXE_haematite"))
        .args(["call", "--socket"])
        .arg(dir.path().join("serve.sock"))
        .args(["branches.list", "{}"])
        .stdout(Stdio::from(std::os::fd::OwnedFd::from(destination)))
        .stderr(Stdio::piped())
        .output()?;
    service.frames()?;
    let stderr = std::str::from_utf8(&output.stderr)?;
    assert_eq!(output.status.code(), Some(3), "{stderr}");
    assert!(
        stderr.contains("answer received") && stderr.contains("stdout"),
        "output failure was not named: {stderr}"
    );
    assert!(
        !stderr.contains("outcome is not known"),
        "received answer described as unknown: {stderr}"
    );
    Ok(())
}