liminal-cli 0.3.0

The liminal command: serve the network, maintain its stores, or serve its discovery MCP over stdio
Documentation
//! SDK-017 R2: list and describe read the record fresh each call, carry the
//! schema strings byte for byte, and name an unserved subject.

use serde_json::json;

use super::{describe, list};
use crate::network::Rail as _;
use crate::network::RailError;
use crate::network::fake::Fake;

const INPUT: &str = r#"{"type":"object","properties":{"text":{"type":"string","description":"le texte à plonger — 日本語"}}}"#;
const OUTPUT: &str =
    r#"{"type":"object","properties":{"vector":{"type":"array","items":{"type":"number"}}}}"#;

fn activities() -> serde_json::Value {
    json!([{ "name": "echo", "input_schema_json": INPUT, "output_schema_json": OUTPUT }])
}

#[test]
fn list_names_the_stations_and_their_subjects() {
    let rail = Fake::new(Fake::record(7, "echo.request", &activities()), &[]);
    let answer = list(&rail);
    assert!(!answer.is_error, "{:?}", answer.structured);
    assert_eq!(answer.structured["rev"], 7);
    let stations = answer.structured["stations"]
        .as_array()
        .cloned()
        .unwrap_or_default();
    assert_eq!(stations.len(), 2);
    assert_eq!(stations[1]["subjects"], json!(["echo.request"]));
    assert_eq!(stations[1]["activities"], json!(["echo"]));
    assert!(stations[0].get("subjects").is_none());
    assert_eq!(rail.counts().read(), [1, 1, 1, 1, 0, 0]);
}

#[test]
fn describe_carries_the_schema_strings_byte_for_byte() {
    let rail = Fake::new(Fake::record(7, "echo.request", &activities()), &[]);
    let answer = describe(&rail, "echo.request");
    assert!(!answer.is_error, "{:?}", answer.structured);
    assert_eq!(answer.structured["station"], "echo.request");
    assert_eq!(
        answer.structured["activities"][0]["input_schema_json"],
        INPUT
    );
    assert_eq!(
        answer.structured["activities"][0]["output_schema_json"],
        OUTPUT
    );
}

#[test]
fn a_station_that_left_is_not_listed() {
    let rail = Fake::new(Fake::record(7, "echo.request", &activities()), &[]);
    assert_eq!(
        list(&rail).structured["stations"].as_array().map(Vec::len),
        Some(2)
    );
    let mut gone = Fake::record(8, "echo.request", &activities());
    gone["stations"].as_array_mut().map(Vec::pop);
    rail.set_topology(Ok(gone));
    let answer = list(&rail);
    assert_eq!(answer.structured["rev"], 8);
    assert_eq!(
        answer.structured["stations"].as_array().map(Vec::len),
        Some(1)
    );
    // Two lists, two reads, nothing held between them.
    assert_eq!(rail.counts().read(), [2, 2, 2, 2, 0, 0]);
}

#[test]
fn describe_of_an_unserved_subject_is_named() {
    let rail = Fake::new(Fake::record(7, "echo.request", &activities()), &[]);
    let answer = describe(&rail, "nobody.request");
    assert!(answer.is_error);
    assert_eq!(
        answer.structured["error"],
        "no station serves nobody.request"
    );
    assert!(answer.structured.get("activities").is_none());
}

#[test]
fn a_record_the_rail_refuses_is_named() {
    let rail = Fake::new(json!({}), &[]);
    rail.set_topology(Err(RailError::RecordTooLarge { limit: 16 }));
    let answer = list(&rail);
    assert!(answer.is_error);
    assert_eq!(
        answer.structured["error"],
        "a topology record over 16 bytes was refused"
    );
}