haematite-cli 0.21.0

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

use super::Config;

fn sample() -> Value {
    json!({
        "listen": "127.0.0.1:8740",
        "origin": "https://studio.example",
        "files": "/opt/haematite/studio",
        "stores": {"wards": "/run/haematite/wards.sock"},
        "lys": {
            "issuer": "https://lys.example/",
            "client_id": "studio",
            "secret_file": "/etc/haematite/studio.secret",
            "redirect": "https://studio.example/callback"
        }
    })
}

fn parsed(value: &Value) -> Result<Config, String> {
    Config::parse(value.to_string().as_bytes())
}

#[test]
fn a_whole_config_is_taken_with_its_defaults() -> Result<(), String> {
    let config = parsed(&sample())?;
    let site = config.site();
    assert_eq!(site.origin, "https://studio.example");
    assert_eq!(site.stores.len(), 1);
    assert_eq!(config.limits().max_connections, 64);
    Ok(())
}

/// Each change is refused alone, with the field it names.
#[test]
fn each_unsafe_field_is_refused_by_name() -> Result<(), String> {
    let cases: [(&str, Value, &str); 9] = [
        ("/listen", json!("0.0.0.0:8740"), "listen"),
        ("/origin", json!("http://studio.example"), "origin"),
        ("/origin", json!("https://studio.example/app"), "origin"),
        ("/files", json!("studio"), "files"),
        ("/stores", json!({}), "stores"),
        (
            "/stores",
            json!({"a/b": "/run/haematite/a.sock"}),
            "store name",
        ),
        ("/stores", json!({"wards": "wards.sock"}), "store socket"),
        (
            "/lys/redirect",
            json!("https://elsewhere.example/callback"),
            "redirect",
        ),
        ("/lys/secret_file", json!("studio.secret"), "secret_file"),
    ];
    for (pointer, value, named) in cases {
        let mut config = sample();
        let slot = config
            .pointer_mut(pointer)
            .ok_or_else(|| format!("the sample has no {pointer}"))?;
        *slot = value.clone();
        let message = parsed(&config)
            .err()
            .ok_or_else(|| format!("{pointer} = {value} was taken"))?;
        assert!(message.contains(named), "{pointer} = {value}: {message}");
    }
    Ok(())
}

#[test]
fn an_unknown_field_is_refused() -> Result<(), String> {
    let mut config = sample();
    config
        .as_object_mut()
        .ok_or("the sample is not an object")?
        .insert("secret".to_owned(), json!("x"));
    assert!(parsed(&config).is_err());
    Ok(())
}