leviath-tools 0.3.2

Native built-in tools for Leviath agents
Documentation
//! Tests for the built-in format checks.
//!
//! Each format gets three cases: something valid, the failure a model actually
//! produces, and proof that the check is well-formedness rather than shape.

use super::*;

#[test]
fn only_the_listed_formats_have_a_builtin() {
    for format in BUILTIN_FORMATS {
        assert!(has_builtin(format), "{format} should have one");
    }
    // A label this crate has never heard of is not validated, which is the
    // normal case: the label is opaque by design.
    for unknown in ["a2ui", "graphql", "markdown", "text/vnd.acme+xml", ""] {
        assert!(!has_builtin(unknown), "{unknown} should not have one");
    }
}

/// A near-miss gets no validation rather than the wrong one.
#[test]
fn a_near_miss_label_is_not_validated() {
    assert!(!has_builtin("json-lines"));
    assert!(!has_builtin("xml-fragment"));
    assert!(check(Some("json-lines"), "not json at all").is_ok());
}

#[test]
fn a_label_is_matched_case_and_whitespace_insensitively() {
    assert!(has_builtin("JSON"));
    assert!(has_builtin(" yaml "));
    assert!(check(Some("JSON"), "{\"a\":1}").is_ok());
}

#[test]
fn no_format_validates_nothing() {
    assert!(check(None, "anything at all").is_ok());
}

// ── json ─────────────────────────────────────────────────────────────────────

#[test]
fn json_accepts_valid_and_refuses_a_fenced_answer() {
    assert!(check(Some("json"), r#"{"summary":"ok","rows":[1,2]}"#).is_ok());
    // The failure that actually happens: the model wraps its answer in fences.
    let fenced = "```json\n{\"a\":1}\n```";
    assert!(check(Some("json"), fenced).is_err());
}

/// Well-formedness, not shape. A JSON document with entirely the wrong contents
/// still passes; that is what a JSON Schema is for.
#[test]
fn json_does_not_check_shape() {
    assert!(check(Some("json"), r#"{"totally":"unexpected"}"#).is_ok());
}

// ── xml ──────────────────────────────────────────────────────────────────────

#[test]
fn xml_accepts_valid_and_refuses_an_unclosed_tag() {
    assert!(check(Some("xml"), "<report><finding severity=\"high\"/></report>").is_ok());
    assert!(check(Some("xml"), "<report><finding></report>").is_err());
}

/// Prose parses as a single text event, so without the element check it would
/// pass as "valid XML". A stage that asked for XML and got a paragraph should
/// hear about it.
#[test]
fn xml_refuses_plain_prose() {
    let err = check(Some("xml"), "Here are my findings, in prose.").expect_err("prose is not XML");
    assert!(err.contains("plain text"), "{err}");
}

#[test]
fn xml_refuses_json_handed_back_by_mistake() {
    assert!(check(Some("xml"), r#"{"finding":"high"}"#).is_err());
}

// ── yaml ─────────────────────────────────────────────────────────────────────

#[test]
fn yaml_accepts_valid_and_refuses_broken_indentation() {
    assert!(check(Some("yaml"), "findings:\n  - severity: high\n").is_ok());
    assert!(check(Some("yml"), "findings:\n  - severity: high\n").is_ok());
    assert!(check(Some("yaml"), "a: [1, 2\nb: 3").is_err());
}

/// YAML alias expansion is exponential and this parser cannot bound it. The
/// check runs inline on the daemon's tick loop over content an agent produced,
/// and an agent can be talked into producing anything by a page it fetched - so
/// a crafted answer would stall every agent in the shared world.
///
/// A document using aliases is skipped rather than rejected: being unable to
/// check something is not evidence it is wrong.
#[test]
fn an_alias_bomb_is_skipped_rather_than_expanded() {
    // Six levels of nine-way aliasing: ~9^6 nodes if expanded. Under 300 bytes.
    let mut bomb = String::from("a: &a [x,x,x,x,x,x,x,x,x]\n");
    let mut prev = "a".to_string();
    for name in ["b", "c", "d", "e", "f", "g"] {
        let refs = vec![format!("*{prev}"); 9].join(",");
        bomb.push_str(&format!("{name}: &{name} [{refs}]\n"));
        prev = name.to_string();
    }
    assert!(bomb.len() < 300, "the input is small; the expansion is not");

    let started = std::time::Instant::now();
    assert!(check(Some("yaml"), &bomb).is_ok(), "skipped, not rejected");
    assert!(
        started.elapsed() < std::time::Duration::from_millis(250),
        "the document must not be expanded at all, took {:?}",
        started.elapsed()
    );
}

#[test]
fn ordinary_yaml_without_aliases_is_still_checked() {
    // The skip must not swallow the check for documents that never risked it.
    assert!(check(Some("yaml"), "a: [1, 2\nb: 3").is_err());
    assert!(check(Some("yaml"), "findings:\n  - severity: high\n").is_ok());
}

/// Over-eager on purpose: mistaking `3 * 4` for an alias costs a skipped check,
/// missing a real one costs the daemon.
#[test]
fn anchor_and_alias_detection_errs_toward_skipping() {
    assert!(uses_anchors_or_aliases("base: &defaults\n  a: 1\n"));
    assert!(uses_anchors_or_aliases("copy: *defaults\n"));
    assert!(uses_anchors_or_aliases("list: [*a, *b]\n"));
    // A multiplication in a scalar reads as an alias here, and that is fine.
    assert!(uses_anchors_or_aliases("note: 3 *4\n"));
    // Nothing that looks like a sigil in a value position.
    assert!(!uses_anchors_or_aliases("findings:\n  - severity: high\n"));
    assert!(!uses_anchors_or_aliases("note: 3 * 4\n"));
}

// ── csv ──────────────────────────────────────────────────────────────────────

#[test]
fn csv_accepts_valid_and_refuses_a_ragged_row() {
    assert!(check(Some("csv"), "name,value\nalpha,1\nbeta,2\n").is_ok());
    // A drifting column count is the failure a consumer actually hits.
    assert!(check(Some("csv"), "name,value\nalpha,1,extra\n").is_err());
}

#[test]
fn csv_refuses_an_empty_answer() {
    assert!(check(Some("csv"), "   \n").is_err());
}

#[test]
fn csv_refuses_an_unbalanced_quote() {
    assert!(check(Some("csv"), "name,value\n\"unterminated,1\n").is_err());
}

// ── toml ─────────────────────────────────────────────────────────────────────

#[test]
fn toml_accepts_valid_and_refuses_a_broken_table() {
    assert!(check(Some("toml"), "[section]\nkey = \"value\"\n").is_ok());
    assert!(check(Some("toml"), "[section\nkey = ").is_err());
}

/// Every failure message is something the agent can act on, since it goes back
/// as the refusal it retries against.
#[test]
fn a_failure_says_what_was_wrong() {
    for (format, bad) in [
        ("json", "not json"),
        ("xml", "<a>"),
        ("yaml", "a: [1, 2\nb: 3"),
        ("csv", "a,b\n1,2,3\n"),
        ("toml", "[oops"),
    ] {
        let outcome = check(Some(format), bad);
        assert!(
            outcome.is_err(),
            "{format} accepted {bad:?}, which it should not"
        );
        assert!(
            !outcome.expect_err("asserted Err just above").is_empty(),
            "{format} gave an empty reason"
        );
    }
}

/// The sigil scanner decides whether a document is skipped rather than parsed,
/// so it has to see an anchor wherever YAML allows one. Missing a position
/// means the parser is handed a document the skip was meant to keep away from
/// it, which is where the expansion cost lives.
#[test]
fn an_anchor_is_recognised_in_every_value_position() {
    for content in [
        "&a x",           // the very start of the document
        "k: &a x",        // after a space
        "k:\t&a x",       // after a tab
        "k:\n  &a x",     // after a newline
        "k:\r\n  &a x",   // after a carriage return
        "k: [&a, b]",     // opening a flow sequence
        "k: {v: &a}",     // opening a flow mapping
        "k: [b, &a]",     // after a comma
        "- &a x",         // a block sequence entry
        "k: *a",          // an alias, not an anchor
        "k: &_private x", // a name starting with an underscore
        "k: &a1 x",       // and one with a digit in it
    ] {
        assert!(
            uses_anchors_or_aliases(content),
            "missed the sigil in {content:?}"
        );
    }
}

/// And it must not fire on an ampersand that is ordinary text, or every
/// document mentioning one would skip the check it was supposed to get.
#[test]
fn an_ampersand_that_is_not_a_sigil_is_left_alone() {
    for content in [
        "k: R&D",          // mid-word
        "k: rock & roll",  // followed by a space
        "k: value &",      // at the very end, with no name after it
        "k: 2 * 3",        // multiplication, followed by a space
        "k: a&&b",         // doubled, so neither is preceded by a separator
        "plain: document", // no sigil at all
    ] {
        assert!(
            !uses_anchors_or_aliases(content),
            "false positive on {content:?}"
        );
    }
}