cf-gears-mini-chat 0.2.1

mini-chat gear: multi-tenant AI chat
Documentation
// Created: 2026-04-07 by Constructor Tech
#![allow(clippy::str_to_string)]
use super::*;
use oagw_sdk::error::ServiceGatewayError;
use oagw_sdk::reason::auth::FailureReason as AuthFailureReason;

#[test]
fn sanitize_removes_provider_response_ids() {
    let msg = "Error in response resp_abc123xyz: rate limit exceeded";
    let sanitized = sanitize_provider_message(msg);
    assert!(!sanitized.contains("resp_abc123xyz"));
    assert!(sanitized.contains("[provider_id]"));
}

#[test]
fn sanitize_removes_urls() {
    let msg = "Error at https://api.openai.com/v1/responses: bad request";
    let sanitized = sanitize_provider_message(msg);
    assert!(!sanitized.contains("https://api.openai.com"));
    assert!(sanitized.contains("[url]"));
}

#[test]
fn sanitize_removes_credentials() {
    let msg = "Auth failed with sk-proj1234567890abcdef";
    let sanitized = sanitize_provider_message(msg);
    assert!(!sanitized.contains("sk-proj1234567890abcdef"));
    assert!(sanitized.contains("[credential]"));
}

#[test]
fn sanitize_mixed_content() {
    let msg = "resp_abc123 at https://api.openai.com with sk-test1234567890";
    let sanitized = sanitize_provider_message(msg);
    assert!(!sanitized.contains("resp_abc123"));
    assert!(!sanitized.contains("https://api.openai.com"));
    assert!(!sanitized.contains("sk-test1234567890"));
}

#[test]
fn raw_detail_preserves_original() {
    let err = LlmProviderError::ProviderError {
        code: "error".to_string(),
        message: "sanitized".to_string(),
        raw_detail: Some(RawDetail(
            "resp_abc123 at https://api.openai.com".to_string(),
        )),
    };
    assert_eq!(
        err.raw_detail(),
        Some("resp_abc123 at https://api.openai.com")
    );
}

#[test]
fn gateway_rate_limit_maps_to_rate_limited() {
    let err = ServiceGatewayError::RateLimited {
        retry_after_secs: None,
    };
    let mapped: LlmProviderError = err.into();
    assert!(matches!(
        mapped,
        LlmProviderError::RateLimited {
            retry_after_secs: None,
        },
    ));
}

#[test]
fn gateway_rate_limit_forwards_retry_hint() {
    let err = ServiceGatewayError::RateLimited {
        retry_after_secs: Some(15),
    };
    let mapped: LlmProviderError = err.into();
    assert!(matches!(
        mapped,
        LlmProviderError::RateLimited {
            retry_after_secs: Some(15),
        },
    ));
}

#[test]
fn gateway_timeout_maps_to_timeout() {
    let mapped: LlmProviderError = ServiceGatewayError::Timeout.into();
    assert!(matches!(mapped, LlmProviderError::Timeout));
}

#[test]
fn gateway_unavailable_maps_to_provider_unavailable() {
    let err = ServiceGatewayError::Unavailable {
        retry_after_secs: Some(5),
    };
    let mapped: LlmProviderError = err.into();
    assert!(matches!(mapped, LlmProviderError::ProviderUnavailable));
}

#[test]
fn gateway_auth_plugin_internal_maps_to_provider_unavailable() {
    // AUTH_PLUGIN_INTERNAL indicates the gateway-side auth machinery
    // itself failed (plugin panic, transport). Treat as transient
    // unavailability, not a hard auth rejection.
    let err = ServiceGatewayError::AuthFailed {
        reason: AuthFailureReason::PluginInternal,
        detail: "plugin crashed".into(),
    };
    let mapped: LlmProviderError = err.into();
    assert!(matches!(mapped, LlmProviderError::ProviderUnavailable));
}

#[test]
fn gateway_auth_plugin_failed_maps_to_provider_error() {
    // PluginFailed = creds rejected; user-facing failure, not transient.
    let err = ServiceGatewayError::AuthFailed {
        reason: AuthFailureReason::PluginFailed,
        detail: "bad token".into(),
    };
    let mapped: LlmProviderError = err.into();
    assert!(matches!(mapped, LlmProviderError::ProviderError { .. }));
}

#[test]
fn gateway_internal_maps_to_provider_error() {
    let err = ServiceGatewayError::Internal {
        detail: "resp_xyz789 failed at https://api.example.com".into(),
    };
    let mapped: LlmProviderError = err.into();
    match mapped {
        LlmProviderError::ProviderError {
            code,
            message,
            raw_detail,
        } => {
            assert_eq!(code, "gateway_error");
            assert!(!message.contains("resp_xyz789"));
            assert!(!message.contains("https://api.example.com"));
            assert!(raw_detail.is_some());
        }
        _ => panic!("expected ProviderError"),
    }
}