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::io::Write;
use std::net::TcpStream;
use std::num::{NonZeroU64, NonZeroUsize};
use std::sync::mpsc;

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

#[test]
fn a_slow_header_connection_is_inside_the_connection_cap() -> TestResult {
    let store = Store::start()?;
    let mut config = config()?;
    config.max_connections = NonZeroUsize::MIN;
    config.header_timeout_ms = NonZeroU64::new(5000).ok_or("header deadline missing")?;
    let (admitted, received) = mpsc::channel();
    let mcp = Server::mcp_signalled(config, store.path.clone(), 131_072, 1, admitted)?;
    let mut partial = TcpStream::connect(mcp.address)?;
    partial.write_all(b"POST /mcp HTTP/1.1\r\n")?;
    received.recv()?;
    let next = Reply::read_frame(request(&mcp, "POST", LIST, true, &[])?)?;
    refusal(&next, 503, "mcp_http_connection_busy")?;
    assert!(store.asked()?.is_empty());
    drop(partial);
    mcp.finish()?;
    store.finish()
}

#[test]
fn an_incomplete_header_ends_on_the_configured_header_deadline() -> TestResult {
    let store = Store::start()?;
    let mut config = config()?;
    config.header_timeout_ms = NonZeroU64::new(50).ok_or("header deadline missing")?;
    let (admitted, received) = mpsc::channel();
    let mcp = Server::mcp_signalled(config, store.path.clone(), 131_072, 1, admitted)?;
    let mut partial = TcpStream::connect(mcp.address)?;
    partial.write_all(b"POST /mcp HTTP/1.1\r\n")?;
    received.recv()?;
    // No peer finishes the headers; the listener must end this read itself.
    refusal(&Reply::read(partial)?, 408, "mcp_http_header_timeout")?;
    assert!(store.asked()?.is_empty());
    mcp.finish()?;
    store.finish()
}

#[test]
fn a_retained_over_cap_peer_cannot_delay_the_next_refusal() -> TestResult {
    let store = Store::start()?;
    let mut config = config()?;
    config.max_connections = NonZeroUsize::MIN;
    config.header_timeout_ms = NonZeroU64::new(5000).ok_or("header deadline missing")?;
    let (admitted, received) = mpsc::channel();
    let mcp = Server::mcp_signalled(config, store.path.clone(), 131_072, 1, admitted)?;
    let mut partial = TcpStream::connect(mcp.address)?;
    partial.write_all(b"POST /mcp HTTP/1.1\r\n")?;
    received.recv()?;
    let started = std::time::Instant::now();
    let mut retained = TcpStream::connect(mcp.address)?;
    refusal(
        &Reply::read_open(&mut retained)?,
        503,
        "mcp_http_connection_busy",
    )?;
    let next = Reply::read(TcpStream::connect(mcp.address)?)?;
    assert!(started.elapsed() < std::time::Duration::from_secs(1));
    refusal(&next, 503, "mcp_http_connection_busy")?;
    assert!(store.asked()?.is_empty());
    drop(retained);
    drop(partial);
    mcp.finish()?;
    store.finish()
}