rig-core 0.44.0

An opinionated library for building LLM powered applications.
Documentation
use super::*;

#[derive(Debug, thiserror::Error)]
#[error("secret detail")]
struct Concrete;

#[test]
fn envelope_is_classified_cloneable_downcastable_and_redacted() {
    let error = ToolExecutionError::provider("operator message")
        .with_model_feedback("safe feedback")
        .with_http_status(503)
        .with_source(Concrete);
    let cloned = error.clone();
    assert_eq!(error.kind(), ToolErrorKind::Provider);
    assert_eq!(error.model_feedback(), Some("safe feedback"));
    assert_eq!(error.http_status(), Some(503));
    assert!(cloned.is::<Concrete>());
    assert!(!format!("{error:?}").contains("secret detail"));
}

#[test]
fn sensitive_diagnostics_can_be_explicitly_redacted() {
    let error = ToolExecutionError::provider("authorization header Bearer secret-token")
        .redact_model_feedback();
    let result = ToolResult::failed(error.clone());

    assert_eq!(error.message(), "authorization header Bearer secret-token");
    assert_eq!(error.model_feedback(), Some("the tool provider failed"));
    assert_eq!(result.output().as_text(), Some("the tool provider failed"));
    assert!(!result.output().render().contains("secret-token"));
}

#[test]
fn skip_refusal_and_permission_failure_are_distinct() {
    let skipped = ToolResult::skipped("policy");
    let refused = ToolResult::failed(ToolExecutionError::refused("tool refused"));
    let permission_failure = ToolResult::failed(ToolExecutionError::permission_denied(
        "authorization failed",
    ));
    assert!(skipped.is_skipped());
    assert!(!skipped.is_refused());
    assert!(refused.is_refused());
    assert!(!refused.is_skipped());
    assert!(!refused.is_error());
    assert!(refused.error().is_none());
    assert!(refused.refusal().is_some_and(|error| error.is_refusal()));
    assert!(permission_failure.is_error());
    assert!(!permission_failure.is_refused());
    assert!(permission_failure.refusal().is_none());
    assert!(permission_failure.is_error_kind(ToolErrorKind::PermissionDenied));
    assert!(!refused.is_error_kind(ToolErrorKind::PermissionDenied));
    assert_eq!(refused.status_name(), "denied");
    assert_eq!(permission_failure.status_name(), "error");
}

#[test]
fn execution_error_debug_redacts_operator_and_model_payloads() {
    let error = ToolExecutionError::provider("Bearer secret-operator-message")
        .with_model_output(ToolOutput::json(serde_json::json!({
            "credential": "secret-model-output"
        })))
        .with_source(Concrete);

    let debug = format!("{error:?}");
    assert!(debug.contains("kind: Provider"));
    assert!(debug.contains("model_output: \"<redacted>\""));
    assert!(debug.contains("source_configured: true"));
    for secret in [
        "secret-operator-message",
        "secret-model-output",
        "secret detail",
    ] {
        assert!(!debug.contains(secret));
    }
}

#[test]
fn debug_redacts_every_tool_result_disposition() {
    let success = ToolResult::success(ToolOutput::text("secret-success"));
    let failure = ToolResult::failed(
        ToolExecutionError::provider("secret-operator").with_model_feedback("secret-model"),
    );
    let skipped = ToolResult::skipped("secret-skip");
    let refused = ToolResult::failed(ToolExecutionError::refused("secret-refusal"));

    for (result, expected_status) in [
        (success, "success"),
        (failure, "error"),
        (skipped, "skipped"),
        (refused, "denied"),
    ] {
        let debug = format!("{result:?}");
        assert!(debug.contains(expected_status));
        for secret in [
            "secret-success",
            "secret-operator",
            "secret-model",
            "secret-skip",
            "secret-refusal",
        ] {
            assert!(!debug.contains(secret));
        }
    }
}