rho-coding-agent 2.15.0

A fast Rust agent harness with a small footprint and opinionated defaults
use crate::tools::canonical_tool_names;
use pretty_assertions::assert_eq;

use super::*;

fn matcher(patterns: &[&str]) -> ToolMatcher {
    ToolMatcher::new(
        patterns
            .iter()
            .map(|pattern| (*pattern).to_owned())
            .collect(),
        canonical_tool_names(),
    )
    .expect("pattern list is valid")
}

#[test]
fn an_omitted_tool_list_matches_every_tool() {
    let matcher = ToolMatcher::any();

    assert!(matcher.matches("bash"));
    assert!(matcher.matches("read_file"));
    assert_eq!(matcher.describe(), "*");
}

#[test]
fn exact_names_match_only_themselves() {
    let matcher = matcher(&["bash", "powershell"]);

    assert!(matcher.matches("bash"));
    assert!(matcher.matches("powershell"));
    assert!(!matcher.matches("read_file"));
    assert_eq!(matcher.describe(), "bash, powershell");
}

#[test]
fn a_trailing_star_matches_by_prefix() {
    let matcher = matcher(&["web*"]);

    assert!(matcher.matches("web_search"));
    assert!(!matcher.matches("bash"));
    assert_eq!(matcher.describe(), "web*");
}

#[test]
fn a_bare_star_matches_everything() {
    let matcher = matcher(&["*"]);

    assert!(matcher.matches("bash"));
    assert!(matcher.matches("anything"));
}

#[test]
fn an_empty_list_is_rejected_rather_than_read_as_all_tools() {
    assert_eq!(
        ToolMatcher::new(Vec::new(), canonical_tool_names()),
        Err(ToolMatcherError::Empty)
    );
}

#[test]
fn a_blank_pattern_is_rejected() {
    assert_eq!(
        ToolMatcher::new(vec!["  ".into()], canonical_tool_names()),
        Err(ToolMatcherError::BlankPattern)
    );
}

#[test]
fn an_unsupported_glob_or_unknown_name_fails_at_load() {
    let unsupported = |pattern: &str| ToolMatcherError::UnsupportedGlob {
        pattern: pattern.into(),
    };
    let unknown = |pattern: &str| ToolMatcherError::UnknownTool {
        pattern: pattern.into(),
    };
    for (case, pattern, expected) in [
        ("leading star", "*_file", unsupported("*_file")),
        ("inner star", "re*d*", unsupported("re*d*")),
        ("wrong case", "Bash", unknown("Bash")),
        ("non-canonical name", "shell", unknown("shell")),
    ] {
        assert_eq!(
            ToolMatcher::new(vec![pattern.into()], canonical_tool_names()),
            Err(expected),
            "{case}"
        );
    }
}

#[test]
fn a_prefix_that_selects_nothing_fails_at_load() {
    assert_eq!(
        ToolMatcher::new(vec!["zzz*".into()], canonical_tool_names()),
        Err(ToolMatcherError::UnknownTool {
            pattern: "zzz*".into()
        })
    );
}