#[cfg(test)]
mod contextual_error_tests {
use crate::core::providers::unified_provider::ProviderError;
#[test]
fn test_contextual_error_display() {
let err = ProviderError::network("openai", "Connection refused")
.with_context("req-12345", Some("gpt-4"));
let display = format!("{}", err);
assert!(display.contains("req-12345"));
assert!(display.contains("Connection refused"));
assert!(display.contains("gpt-4"));
}
#[test]
fn test_contextual_error_methods() {
let err = ProviderError::rate_limit("anthropic", Some(60)).with_context("req-abc", None);
assert!(err.is_retryable());
assert_eq!(err.retry_delay(), Some(60));
assert_eq!(err.http_status(), 429);
assert_eq!(err.provider(), "anthropic");
assert_eq!(err.request_id(), "req-abc");
assert!(err.model().is_none());
}
#[test]
fn test_to_error_response() {
let err = ProviderError::authentication("openai", "Invalid API key")
.with_context("req-xyz", Some("gpt-4-turbo"));
let response = err.to_error_response();
assert_eq!(response["error"]["request_id"], "req-xyz");
assert_eq!(response["error"]["model"], "gpt-4-turbo");
assert_eq!(response["error"]["code"], 401);
assert_eq!(response["error"]["provider"], "openai");
}
}
#[cfg(test)]
mod provider_error_tests {
use crate::core::providers::unified_provider::ProviderError;
#[test]
fn test_authentication_factory() {
let err = ProviderError::authentication("openai", "Invalid API key");
assert_eq!(err.provider(), "openai");
assert_eq!(err.http_status(), 401);
assert!(!err.is_retryable());
}
#[test]
fn test_rate_limit_factory() {
let err = ProviderError::rate_limit("anthropic", Some(60));
assert_eq!(err.provider(), "anthropic");
assert_eq!(err.http_status(), 429);
assert!(err.is_retryable());
assert_eq!(err.retry_delay(), Some(60));
}
#[test]
fn test_rate_limit_factory_no_retry() {
let err = ProviderError::rate_limit("anthropic", None);
assert!(err.retry_delay().is_none());
}
#[test]
fn test_rate_limit_with_limits() {
let err = ProviderError::rate_limit_with_limits(
"openai",
Some(60),
Some(100),
Some(10000),
Some(0.9),
);
assert_eq!(err.provider(), "openai");
assert!(err.is_retryable());
}
#[test]
fn test_quota_exceeded_factory() {
let err = ProviderError::quota_exceeded("vertex_ai", "Monthly quota exceeded");
assert_eq!(err.provider(), "vertex_ai");
assert_eq!(err.http_status(), 402);
assert!(!err.is_retryable());
}
#[test]
fn test_model_not_found_factory() {
let err = ProviderError::model_not_found("openai", "gpt-5");
assert_eq!(err.provider(), "openai");
assert_eq!(err.http_status(), 404);
assert!(!err.is_retryable());
}
#[test]
fn test_invalid_request_factory() {
let err = ProviderError::invalid_request("anthropic", "Missing messages");
assert_eq!(err.provider(), "anthropic");
assert_eq!(err.http_status(), 400);
assert!(!err.is_retryable());
}
#[test]
fn test_network_factory() {
let err = ProviderError::network("openai", "Connection refused");
assert_eq!(err.provider(), "openai");
assert_eq!(err.http_status(), 503);
assert!(err.is_retryable());
assert_eq!(err.retry_delay(), Some(1));
}
#[test]
fn test_provider_unavailable_factory() {
let err = ProviderError::provider_unavailable("anthropic", "Service down");
assert_eq!(err.provider(), "anthropic");
assert!(err.is_retryable());
assert_eq!(err.retry_delay(), Some(5));
}
#[test]
fn test_not_supported_factory() {
let err = ProviderError::not_supported("openai", "vision");
assert_eq!(err.provider(), "openai");
assert_eq!(err.http_status(), 405);
assert!(!err.is_retryable());
}
#[test]
fn test_not_implemented_factory() {
let err = ProviderError::not_implemented("anthropic", "streaming");
assert_eq!(err.provider(), "anthropic");
assert_eq!(err.http_status(), 501);
assert!(!err.is_retryable());
}
#[test]
fn test_configuration_factory() {
let err = ProviderError::configuration("openai", "Missing API key");
assert_eq!(err.provider(), "openai");
assert_eq!(err.http_status(), 400);
assert!(!err.is_retryable());
}
#[test]
fn test_serialization_factory() {
let err = ProviderError::serialization("anthropic", "Invalid JSON");
assert_eq!(err.provider(), "anthropic");
assert_eq!(err.http_status(), 500);
assert!(!err.is_retryable());
}
#[test]
fn test_timeout_factory() {
let err = ProviderError::timeout("openai", "Request timed out after 30s");
assert_eq!(err.provider(), "openai");
assert_eq!(err.http_status(), 503);
assert!(err.is_retryable());
assert_eq!(err.retry_delay(), Some(1));
}
#[test]
fn test_context_length_exceeded() {
let err = ProviderError::context_length_exceeded("openai", 4096, 5000);
assert_eq!(err.provider(), "openai");
assert_eq!(err.http_status(), 413);
assert!(!err.is_retryable());
}
#[test]
fn test_api_error() {
let err = ProviderError::api_error("anthropic", 500, "Internal server error");
assert_eq!(err.provider(), "anthropic");
assert_eq!(err.http_status(), 500);
assert!(err.is_retryable());
}
#[test]
fn test_api_error_429() {
let err = ProviderError::api_error("openai", 429, "Rate limited");
assert!(err.is_retryable());
assert_eq!(err.retry_delay(), Some(60));
}
#[test]
fn test_api_error_400() {
let err = ProviderError::api_error("openai", 400, "Bad request");
assert!(!err.is_retryable());
assert!(err.retry_delay().is_none());
}
#[test]
fn test_token_limit_exceeded() {
let err = ProviderError::token_limit_exceeded("openai", "Max tokens exceeded");
assert_eq!(err.provider(), "openai");
assert_eq!(err.http_status(), 413);
assert!(!err.is_retryable());
}
#[test]
fn test_feature_disabled() {
let err = ProviderError::feature_disabled("vertex_ai", "code_execution");
assert_eq!(err.provider(), "vertex_ai");
assert_eq!(err.http_status(), 403);
assert!(!err.is_retryable());
}
#[test]
fn test_deployment_error() {
let err = ProviderError::deployment_error("my-deployment", "Deployment not found");
assert_eq!(err.provider(), "azure");
assert_eq!(err.http_status(), 404);
assert!(err.is_retryable());
}
#[test]
fn test_response_parsing() {
let err = ProviderError::response_parsing("openai", "Invalid JSON response");
assert_eq!(err.provider(), "openai");
assert_eq!(err.http_status(), 502);
assert!(!err.is_retryable());
}
#[test]
fn test_routing_error() {
let err = ProviderError::routing_error(
"openrouter",
vec!["openai".to_string(), "anthropic".to_string()],
"All providers failed",
);
assert_eq!(err.provider(), "openrouter");
assert_eq!(err.http_status(), 503);
assert!(!err.is_retryable());
}
#[test]
fn test_transformation_error() {
let err = ProviderError::transformation_error(
"openrouter",
"openai",
"anthropic",
"Invalid message format",
);
assert_eq!(err.provider(), "openrouter");
assert_eq!(err.http_status(), 500);
assert!(!err.is_retryable());
}
#[test]
fn test_content_filtered_not_retryable() {
let err = ProviderError::content_filtered("openai", "Content policy violation", None, None);
assert_eq!(err.provider(), "openai");
assert_eq!(err.http_status(), 400);
assert!(!err.is_retryable());
}
#[test]
fn test_content_filtered_retryable() {
let err = ProviderError::content_filtered(
"openai",
"Content might be inappropriate",
Some(vec!["violence".to_string()]),
Some(true),
);
assert!(err.is_retryable());
assert_eq!(err.retry_delay(), Some(10));
}
#[test]
fn test_cancelled() {
let err = ProviderError::cancelled(
"openai",
"chat_completion",
Some("User cancelled".to_string()),
);
assert_eq!(err.provider(), "openai");
assert_eq!(err.http_status(), 499);
assert!(!err.is_retryable());
}
#[test]
fn test_streaming_error() {
let err = ProviderError::streaming_error(
"anthropic",
"chat",
Some(100),
Some("last chunk".to_string()),
"Stream interrupted",
);
assert_eq!(err.provider(), "anthropic");
assert_eq!(err.http_status(), 500);
assert!(err.is_retryable());
assert_eq!(err.retry_delay(), Some(2));
}
#[test]
fn test_other_error() {
let err = ProviderError::other("unknown", "Unknown error occurred");
assert_eq!(err.provider(), "unknown");
assert_eq!(err.http_status(), 500);
assert!(!err.is_retryable());
}
#[test]
fn test_error_type_strings() {
use crate::core::types::errors::ProviderErrorTrait;
assert_eq!(
ProviderError::authentication("a", "b").error_type(),
"authentication"
);
assert_eq!(
ProviderError::rate_limit("a", None).error_type(),
"rate_limit"
);
assert_eq!(
ProviderError::quota_exceeded("a", "b").error_type(),
"quota_exceeded"
);
assert_eq!(
ProviderError::model_not_found("a", "b").error_type(),
"model_not_found"
);
assert_eq!(ProviderError::network("a", "b").error_type(), "network");
assert_eq!(ProviderError::timeout("a", "b").error_type(), "timeout");
}
#[test]
fn test_error_display_authentication() {
let err = ProviderError::authentication("openai", "Invalid API key");
let display = format!("{}", err);
assert!(display.contains("openai"));
assert!(display.contains("Invalid API key"));
}
#[test]
fn test_error_display_rate_limit() {
let err = ProviderError::rate_limit("anthropic", Some(60));
let display = format!("{}", err);
assert!(display.contains("anthropic"));
assert!(display.contains("Rate limit"));
}
#[test]
fn test_error_display_model_not_found() {
let err = ProviderError::model_not_found("openai", "gpt-5");
let display = format!("{}", err);
assert!(display.contains("openai"));
assert!(display.contains("gpt-5"));
}
#[test]
fn test_from_serde_json_error() {
let json_err = serde_json::from_str::<serde_json::Value>("invalid json").unwrap_err();
let err: ProviderError = json_err.into();
assert_eq!(err.provider(), "unknown");
}
#[test]
fn test_http_status_mapping() {
assert_eq!(ProviderError::authentication("a", "b").http_status(), 401);
assert_eq!(ProviderError::rate_limit("a", None).http_status(), 429);
assert_eq!(ProviderError::quota_exceeded("a", "b").http_status(), 402);
assert_eq!(ProviderError::model_not_found("a", "b").http_status(), 404);
assert_eq!(ProviderError::invalid_request("a", "b").http_status(), 400);
assert_eq!(ProviderError::not_supported("a", "b").http_status(), 405);
assert_eq!(ProviderError::not_implemented("a", "b").http_status(), 501);
assert_eq!(ProviderError::network("a", "b").http_status(), 503);
assert_eq!(ProviderError::serialization("a", "b").http_status(), 500);
}
#[test]
fn test_retryable_errors() {
assert!(ProviderError::network("a", "b").is_retryable());
assert!(ProviderError::timeout("a", "b").is_retryable());
assert!(ProviderError::rate_limit("a", None).is_retryable());
assert!(ProviderError::provider_unavailable("a", "b").is_retryable());
assert!(ProviderError::deployment_error("a", "b").is_retryable());
assert!(ProviderError::streaming_error("a", "b", None, None, "c").is_retryable());
}
#[test]
fn test_non_retryable_errors() {
assert!(!ProviderError::authentication("a", "b").is_retryable());
assert!(!ProviderError::quota_exceeded("a", "b").is_retryable());
assert!(!ProviderError::model_not_found("a", "b").is_retryable());
assert!(!ProviderError::invalid_request("a", "b").is_retryable());
assert!(!ProviderError::not_supported("a", "b").is_retryable());
assert!(!ProviderError::configuration("a", "b").is_retryable());
}
#[test]
fn test_error_clone() {
let err = ProviderError::authentication("openai", "Invalid key");
let cloned = err.clone();
assert_eq!(err.provider(), cloned.provider());
}
#[test]
fn test_error_debug() {
let err = ProviderError::authentication("openai", "Invalid key");
let debug = format!("{:?}", err);
assert!(debug.contains("Authentication"));
}
}