mentra 0.18.1

An agent runtime for tool-using LLM applications
Documentation
//! Public-API tests for skill registration and enumeration.
//!
//! These exercise what a host can actually reach: registering roots through
//! `Runtime`, listing what loaded, and naming the error type in its own
//! signatures.

use std::{
    fs,
    path::{Path, PathBuf},
    sync::atomic::{AtomicU64, Ordering},
    time::{SystemTime, UNIX_EPOCH},
};

use mentra::{BuiltinProvider, Runtime, SkillInfo, SkillLoadError};

static NEXT_ID: AtomicU64 = AtomicU64::new(0);

fn temp_dir(label: &str) -> PathBuf {
    let unique = NEXT_ID.fetch_add(1, Ordering::Relaxed);
    let stamp = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .expect("system time")
        .as_nanos();
    let path = std::env::temp_dir().join(format!("mentra-skills-api-{label}-{stamp}-{unique}"));
    fs::create_dir_all(&path).expect("create temp dir");
    path
}

fn write_skill(root: &Path, dir: &str, name: &str, description: &str, body: &str) {
    let skill_dir = root.join(dir);
    fs::create_dir_all(&skill_dir).expect("create skill dir");
    fs::write(
        skill_dir.join("SKILL.md"),
        format!("---\nname: {name}\ndescription: {description}\n---\n{body}\n"),
    )
    .expect("write skill");
}

fn runtime() -> Runtime {
    Runtime::builder()
        .with_provider(BuiltinProvider::OpenAI, "test-key")
        .build()
        .expect("runtime builds")
}

/// The error type must be nameable by a caller — this signature is the test.
fn register(runtime: &Runtime, path: &Path) -> Result<(), SkillLoadError> {
    runtime.register_skills_dir(path)
}

#[test]
fn a_host_can_name_the_error_type() {
    let runtime = runtime();
    let missing = temp_dir("nameable").join("does-not-exist");

    let error = register(&runtime, &missing).expect_err("an unreadable root is an error");

    // And match on it, which is the point of it being an enum.
    assert!(matches!(error, SkillLoadError::ReadDir { .. }));
}

#[test]
fn registering_a_second_root_keeps_the_first() {
    let workspace = temp_dir("layer-workspace");
    write_skill(&workspace, "review", "review", "project review", "PROJECT");
    let global = temp_dir("layer-global");
    write_skill(&global, "review", "review", "personal review", "PERSONAL");
    write_skill(&global, "deploy", "deploy", "personal deploy", "DEPLOY");

    let runtime = runtime();
    runtime
        .register_skills_dirs([workspace.as_path(), global.as_path()])
        .expect("both roots register");

    let skills = runtime.skills();
    let names: Vec<&str> = skills.iter().map(|skill| skill.name.as_str()).collect();
    assert_eq!(names, vec!["deploy", "review"]);

    let review = skills
        .iter()
        .find(|skill| skill.name == "review")
        .expect("review present");
    assert_eq!(
        review.description, "project review",
        "the earlier root must win the collision"
    );
    assert!(review.path.starts_with(&workspace));
}

#[test]
fn enumeration_reports_name_description_and_source() {
    let root = temp_dir("enumerate");
    write_skill(&root, "haiku", "haiku", "writes haiku", "BODY");

    let runtime = runtime();
    runtime.register_skills_dir(&root).expect("registers");

    let skills = runtime.skills();
    assert_eq!(skills.len(), 1);
    let SkillInfo {
        name,
        description,
        path,
    } = &skills[0];
    assert_eq!(name, "haiku");
    assert_eq!(description, "writes haiku");
    assert_eq!(path, &root.join("haiku").join("SKILL.md"));
}

#[test]
fn a_runtime_without_skills_lists_none() {
    assert!(runtime().skills().is_empty());
}

#[test]
fn a_duplicate_name_inside_one_root_is_still_an_error() {
    let root = temp_dir("duplicate");
    write_skill(&root, "first", "shared", "one", "A");
    write_skill(&root, "second", "shared", "two", "B");

    let error = runtime()
        .register_skills_dir(&root)
        .expect_err("a repeated name in one root is a mistake");

    assert!(matches!(error, SkillLoadError::DuplicateSkillName { .. }));
}

#[test]
fn roots_before_a_failing_one_stay_registered() {
    let good = temp_dir("partial-good");
    write_skill(&good, "keep", "keep", "kept", "BODY");
    let missing = temp_dir("partial-missing").join("absent");

    let runtime = runtime();
    let error = runtime
        .register_skills_dirs([good.as_path(), missing.as_path()])
        .expect_err("the second root fails");

    assert!(matches!(error, SkillLoadError::ReadDir { .. }));
    assert_eq!(
        runtime.skills().len(),
        1,
        "the root that loaded before the failure stays registered"
    );
}