roboticus-api 0.11.3

HTTP routes, WebSocket, auth, rate limiting, and dashboard for the Roboticus agent runtime
Documentation
//! Track 3 Task 7 — integration tests for strict skill validation and CompositionResult.
//!
//! Verifies:
//! - CompositionResult serializes and round-trips through serde correctly.
//! - CompositionResult correctly reports completeness based on missing_skills.
//! - validate_skills_against_registry_strict rejects phantom (nonexistent) skills.
//! - validate_skills_against_registry_strict accepts skills that exist in the registry.

use super::*;

/// CompositionResult serializes and deserializes correctly (round-trip).
#[test]
fn composition_result_round_trips() {
    use roboticus_core::composition::CompositionResult;

    let result = CompositionResult {
        agent_name: "test-agent".into(),
        validated_skills: vec!["real-skill".into()],
        missing_skills: vec!["phantom-skill".into()],
        auto_created_skills: vec![],
        warnings: vec![],
    };

    let json = serde_json::to_string(&result).unwrap();
    let parsed: CompositionResult = serde_json::from_str(&json).unwrap();
    assert_eq!(parsed.missing_skills, vec!["phantom-skill"]);
    assert!(!parsed.is_complete());
}

/// CompositionResult with no missing skills is considered complete.
#[test]
fn composition_result_complete_when_no_missing() {
    use roboticus_core::composition::CompositionResult;

    let result = CompositionResult {
        agent_name: "complete-agent".into(),
        validated_skills: vec!["skill-a".into(), "skill-b".into()],
        missing_skills: vec![],
        auto_created_skills: vec![],
        warnings: vec!["some warning".into()],
    };

    assert!(result.is_complete());
}

/// validate_skills_against_registry_strict rejects a nonexistent phantom skill
/// and surfaces it in the error's missing_skills list.
#[test]
fn strict_validation_rejects_phantom_skills() {
    let state = test_state();

    let result = crate::api::routes::subagent_integrity::validate_skills_against_registry_strict(
        &state,
        &["nonexistent-phantom-skill".to_string()],
    );

    assert!(result.is_err(), "should reject nonexistent skill");
    let err = result.unwrap_err();
    assert!(
        err.missing_skills
            .contains(&"nonexistent-phantom-skill".to_string()),
        "error should list the missing skill: {:?}",
        err.missing_skills
    );
}

/// validate_skills_against_registry_strict accepts a skill that exists in the registry.
#[test]
fn strict_validation_accepts_known_skills() {
    let state = test_state();

    // Get a known skill from the registry.
    let registry = crate::api::routes::subagent_integrity::skill_registry_names(&state);
    if let Some(known_skill) = registry.into_iter().next() {
        let result =
            crate::api::routes::subagent_integrity::validate_skills_against_registry_strict(
                &state,
                std::slice::from_ref(&known_skill),
            );
        assert!(
            result.is_ok(),
            "should accept known skill '{known_skill}': {:?}",
            result.unwrap_err()
        );
    }
    // If the registry is empty (unlikely in test environment), the test trivially passes.
}