liminal-cli 0.2.1

The liminal command: serve the network, maintain its stores, or serve its discovery MCP over stdio
Documentation
//! SDK-019 R1: each lifecycle tool is one request on its lifecycle subject
//! carrying an operation id, answered verbatim; start wakes a held service.

use serde_json::{Map, Value, json};

use super::{tool, tool_entries};
use crate::network::fake::{Answer, Fake};

fn arguments(value: &Value) -> Map<String, Value> {
    value.as_object().cloned().unwrap_or_default()
}

#[test]
fn the_five_tools_are_in_the_table_with_their_schemas() {
    let names: Vec<String> = tool_entries()
        .iter()
        .filter_map(|entry| entry["name"].as_str().map(str::to_owned))
        .collect();
    assert_eq!(names, ["services", "register", "start", "stop", "retire"]);
    let register = &tool_entries()[1]["inputSchema"];
    assert_eq!(register["required"], json!(["template", "name"]));
    assert_eq!(tool_entries()[2]["inputSchema"]["required"], json!(["id"]));
    let table = crate::mcp::tools::tool_table();
    assert_eq!(table.as_array().map(Vec::len), Some(11));
}

#[test]
fn register_sends_create_with_the_operation_and_answers_verbatim() {
    let reply = json!({ "outcome": "acted", "service": { "id": "svc-1" } });
    let rail = Fake::new(
        json!({}),
        &[("services.create", Answer::Reply(reply.clone()))],
    );
    let answer = tool(
        &rail,
        "register",
        &arguments(&json!({ "template": "echo", "name": "one", "operation": "op-7" })),
    );
    assert_eq!(answer.as_ref().map(|a| a.structured.clone()), Some(reply));
    assert_eq!(
        rail.pushed(),
        vec![(
            "services.create".to_owned(),
            json!({ "operation": "op-7", "payload": { "template": "echo", "name": "one" } })
        )]
    );
}

#[test]
fn a_refusal_is_the_servers_words() {
    let rail = Fake::new(
        json!({}),
        &[("services.hold", Answer::Refuse("missing_grant".to_owned()))],
    );
    let answer = tool(&rail, "stop", &arguments(&json!({ "id": "svc-1" })));
    assert!(answer.as_ref().is_some_and(|a| a.is_error));
    let words = answer.map(|a| a.structured.to_string()).unwrap_or_default();
    assert!(words.contains("missing_grant"), "{words}");
}

#[test]
fn start_wakes_a_held_service() {
    let rail = Fake::new(
        json!({}),
        &[
            (
                "services.deploy",
                Answer::Refuse("not_now: deploy does not apply to a service Held".to_owned()),
            ),
            (
                "services.wake",
                Answer::Reply(json!({ "outcome": "acted" })),
            ),
        ],
    );
    let answer = tool(
        &rail,
        "start",
        &arguments(&json!({ "id": "svc-1", "operation": "op-1" })),
    );
    assert_eq!(answer.map(|a| a.is_error), Some(false));
    let subjects: Vec<String> = rail.pushed().into_iter().map(|(s, _)| s).collect();
    assert_eq!(subjects, ["services.deploy", "services.wake"]);
}

#[test]
fn a_missing_argument_sends_nothing_and_another_tool_is_not_ours() {
    let rail = Fake::new(json!({}), &[]);
    let answer = tool(&rail, "retire", &arguments(&json!({})));
    assert!(answer.is_some_and(|a| a.is_error));
    assert!(rail.pushed().is_empty());
    assert!(tool(&rail, "call", &arguments(&json!({}))).is_none());
}

#[test]
fn an_operation_is_made_when_none_is_given() {
    let rail = Fake::new(json!({}), &[("services.list", Answer::Echo)]);
    let answer = tool(&rail, "services", &Map::new());
    let operation = answer
        .and_then(|a| {
            a.structured["echoed"]["operation"]
                .as_str()
                .map(str::to_owned)
        })
        .unwrap_or_default();
    assert!(
        operation.starts_with("op-") && operation.len() == 35,
        "{operation}"
    );
}