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 std::net::Shutdown;
use std::sync::mpsc;
use std::time::Duration;

use super::listener;

use super::fixtures::{LIST, Reply, Server, TestResult, config, refusal, request};
use super::native_call_fixture::Store;

#[test]
fn a_disconnected_caller_keeps_its_permit_until_the_native_call_returns() -> TestResult {
    let native = Store::held()?;
    let (finished, completion) = mpsc::channel();
    let server = listener::Server::new(
        config()?,
        native.path.clone(),
        Duration::from_secs(1),
        131_072,
        1,
    )?
    .with_completion_signal(finished)?;
    let mcp = Server::from_mcp(server)?;
    let release = native.release_on_exit();
    let call = r#"{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"describe_store","arguments":{"selection":{"branch":"main","version":null},"page":{"rows":64,"bytes":65536},"after":{}}}}"#;
    let caller = request(&mcp, "POST", call, true, &[])?;
    native.entered.recv()?;
    caller.shutdown(Shutdown::Both)?;
    drop(caller);
    let next = Reply::read(request(&mcp, "POST", LIST, true, &[])?)?;
    refusal(&next, 503, "mcp_http_busy")?;
    // The busy refusal comes before the store: only the held call presented.
    assert_eq!(native.presented()?, 1);
    native.open()?;
    completion.recv()?;
    assert_eq!(
        Reply::read(request(&mcp, "POST", LIST, true, &[])?)?.status,
        200
    );
    // The held call's next verb presented on its own connection, then the list.
    assert_eq!(native.presented()?, 3, "{:?}", native.asked()?);
    drop(release);
    mcp.finish()?;
    native.finish()
}