use super::*;
use actix_web::http::StatusCode;
#[test]
fn test_error_detail_creation() {
let detail = GatewayErrorDetail {
code: "AUTH_ERROR".to_string(),
canonical_code: "AUTHENTICATION".to_string(),
retryable: false,
message: "Authentication failed".to_string(),
timestamp: 1704067200,
request_id: Some("req-12345".to_string()),
};
assert_eq!(detail.code, "AUTH_ERROR");
assert_eq!(detail.canonical_code, "AUTHENTICATION");
assert!(!detail.retryable);
assert_eq!(detail.message, "Authentication failed");
assert_eq!(detail.timestamp, 1704067200);
assert_eq!(detail.request_id, Some("req-12345".to_string()));
}
#[test]
fn test_error_detail_without_request_id() {
let detail = GatewayErrorDetail {
code: "VALIDATION_ERROR".to_string(),
canonical_code: "INVALID_REQUEST".to_string(),
retryable: false,
message: "Invalid input".to_string(),
timestamp: chrono::Utc::now().timestamp(),
request_id: None,
};
assert!(detail.request_id.is_none());
assert!(detail.timestamp > 0);
}
#[test]
fn test_error_detail_serialization() {
let detail = GatewayErrorDetail {
code: "NOT_FOUND".to_string(),
canonical_code: "NOT_FOUND".to_string(),
retryable: false,
message: "Resource not found".to_string(),
timestamp: 1704067200,
request_id: Some("req-abc".to_string()),
};
let json = serde_json::to_value(&detail).unwrap();
assert_eq!(json["code"], "NOT_FOUND");
assert_eq!(json["canonical_code"], "NOT_FOUND");
assert_eq!(json["retryable"], false);
assert_eq!(json["message"], "Resource not found");
assert_eq!(json["timestamp"], 1704067200);
assert_eq!(json["request_id"], "req-abc");
}
#[test]
fn test_error_detail_serialization_null_request_id() {
let detail = GatewayErrorDetail {
code: "ERROR".to_string(),
canonical_code: "INTERNAL".to_string(),
retryable: false,
message: "Some error".to_string(),
timestamp: 1704067200,
request_id: None,
};
let json = serde_json::to_value(&detail).unwrap();
assert!(json["request_id"].is_null());
}
#[test]
fn test_error_response_creation() {
let response = GatewayErrorResponse {
error: GatewayErrorDetail {
code: "INTERNAL_ERROR".to_string(),
canonical_code: "INTERNAL".to_string(),
retryable: false,
message: "An internal error occurred".to_string(),
timestamp: 1704067200,
request_id: None,
},
};
assert_eq!(response.error.code, "INTERNAL_ERROR");
}
#[test]
fn test_error_response_serialization() {
let response = GatewayErrorResponse {
error: GatewayErrorDetail {
code: "BAD_REQUEST".to_string(),
canonical_code: "INVALID_REQUEST".to_string(),
retryable: false,
message: "Invalid parameters".to_string(),
timestamp: 1704067200,
request_id: Some("req-xyz".to_string()),
},
};
let json = serde_json::to_value(&response).unwrap();
assert!(json["error"].is_object());
assert_eq!(json["error"]["code"], "BAD_REQUEST");
assert_eq!(json["error"]["canonical_code"], "INVALID_REQUEST");
assert_eq!(json["error"]["retryable"], false);
assert_eq!(json["error"]["message"], "Invalid parameters");
}
#[test]
fn test_error_response_json_string() {
let response = GatewayErrorResponse {
error: GatewayErrorDetail {
code: "RATE_LIMIT".to_string(),
canonical_code: "RATE_LIMITED".to_string(),
retryable: true,
message: "Too many requests".to_string(),
timestamp: 1704067200,
request_id: None,
},
};
let json_str = serde_json::to_string(&response).unwrap();
assert!(json_str.contains("RATE_LIMIT"));
assert!(json_str.contains("Too many requests"));
}
#[test]
fn test_gateway_error_config_response() {
let error = GatewayError::Config("Invalid configuration".to_string());
let response = error.error_response();
assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
}
#[test]
fn test_gateway_error_internal_response() {
let error = GatewayError::Internal("Internal server error".to_string());
let response = error.error_response();
assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
}
#[test]
fn test_gateway_error_auth_response() {
let error = GatewayError::Auth("Invalid token".to_string());
let response = error.error_response();
assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
}
#[test]
fn test_gateway_error_forbidden_response2() {
let error = GatewayError::Forbidden("Permission denied".to_string());
let response = error.error_response();
assert_eq!(response.status(), StatusCode::FORBIDDEN);
}
#[test]
fn test_gateway_error_rate_limit_response() {
let error = GatewayError::RateLimit {
message: "Rate limit exceeded".to_string(),
retry_after: Some(60),
rpm_limit: Some(100),
tpm_limit: Some(50000),
};
let response = error.error_response();
assert_eq!(response.status(), StatusCode::TOO_MANY_REQUESTS);
assert_eq!(
response
.headers()
.get("Retry-After")
.unwrap()
.to_str()
.unwrap(),
"60"
);
assert_eq!(
response
.headers()
.get("X-RateLimit-Limit-Requests")
.unwrap()
.to_str()
.unwrap(),
"100"
);
assert_eq!(
response
.headers()
.get("X-RateLimit-Limit-Tokens")
.unwrap()
.to_str()
.unwrap(),
"50000"
);
}
#[test]
fn test_gateway_error_validation_response() {
let error = GatewayError::Validation("Invalid input".to_string());
let response = error.error_response();
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
}
#[test]
fn test_gateway_error_not_found_response() {
let error = GatewayError::NotFound("Resource not found".to_string());
let response = error.error_response();
assert_eq!(response.status(), StatusCode::NOT_FOUND);
}
#[test]
fn test_gateway_error_conflict_response() {
let error = GatewayError::Conflict("Resource conflict".to_string());
let response = error.error_response();
assert_eq!(response.status(), StatusCode::CONFLICT);
}
#[test]
fn test_gateway_error_bad_request_response() {
let error = GatewayError::BadRequest("Bad request".to_string());
let response = error.error_response();
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
}
#[test]
fn test_gateway_error_timeout_response() {
let error = GatewayError::Timeout("Request timeout".to_string());
let response = error.error_response();
assert_eq!(response.status(), StatusCode::REQUEST_TIMEOUT);
}
#[test]
fn test_gateway_error_provider_unavailable_response() {
let error = GatewayError::Unavailable("Provider down".to_string());
let response = error.error_response();
assert_eq!(response.status(), StatusCode::SERVICE_UNAVAILABLE);
}
#[test]
fn test_gateway_error_circuit_breaker_response() {
let error = GatewayError::Unavailable("Circuit open".to_string());
let response = error.error_response();
assert_eq!(response.status(), StatusCode::SERVICE_UNAVAILABLE);
}
#[test]
fn test_gateway_error_network_response() {
let error = GatewayError::Network("Network error".to_string());
let response = error.error_response();
assert_eq!(response.status(), StatusCode::BAD_GATEWAY);
}
#[test]
fn test_gateway_error_parsing_response() {
let error = GatewayError::Validation("Parse error".to_string());
let response = error.error_response();
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
}
#[test]
fn test_gateway_error_not_implemented_response() {
let error = GatewayError::NotImplemented("Feature not implemented".to_string());
let response = error.error_response();
assert_eq!(response.status(), StatusCode::NOT_IMPLEMENTED);
}
#[test]
fn test_gateway_error_forbidden_response() {
let error = GatewayError::Forbidden("Forbidden".to_string());
let response = error.error_response();
assert_eq!(response.status(), StatusCode::FORBIDDEN);
}
#[test]
fn test_gateway_error_external_response() {
let error = GatewayError::Network("External service error".to_string());
let response = error.error_response();
assert_eq!(response.status(), StatusCode::BAD_GATEWAY);
}
#[test]
fn test_gateway_error_no_providers_available_response() {
let error = GatewayError::Unavailable("No providers".to_string());
let response = error.error_response();
assert_eq!(response.status(), StatusCode::SERVICE_UNAVAILABLE);
}
#[test]
fn test_gateway_error_provider_not_found_response() {
let error = GatewayError::NotFound("Provider not found".to_string());
let response = error.error_response();
assert_eq!(response.status(), StatusCode::NOT_FOUND);
}
#[test]
fn test_gateway_error_no_providers_for_model_response() {
let error = GatewayError::BadRequest("No providers for model".to_string());
let response = error.error_response();
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
}
#[test]
fn test_gateway_error_no_healthy_providers_response() {
let error = GatewayError::Unavailable("No healthy providers".to_string());
let response = error.error_response();
assert_eq!(response.status(), StatusCode::SERVICE_UNAVAILABLE);
}
#[test]
fn test_provider_error_rate_limit_response() {
let provider_error = ProviderError::RateLimit {
provider: "openai",
message: "Rate limit exceeded".to_string(),
retry_after: Some(60),
rpm_limit: Some(100),
tpm_limit: Some(10000),
current_usage: Some(0.95),
};
let error = GatewayError::Provider(provider_error);
let response = error.error_response();
assert_eq!(response.status(), StatusCode::TOO_MANY_REQUESTS);
}
#[test]
fn test_provider_error_quota_exceeded_response() {
let provider_error = ProviderError::QuotaExceeded {
provider: "anthropic",
message: "Monthly quota exceeded".to_string(),
};
let error = GatewayError::Provider(provider_error);
let response = error.error_response();
assert_eq!(response.status(), StatusCode::PAYMENT_REQUIRED);
}
#[test]
fn test_provider_error_model_not_found_response() {
let provider_error = ProviderError::ModelNotFound {
provider: "openai",
model: "gpt-5".to_string(),
};
let error = GatewayError::Provider(provider_error);
let response = error.error_response();
assert_eq!(response.status(), StatusCode::NOT_FOUND);
}
#[test]
fn test_provider_error_invalid_request_response() {
let provider_error = ProviderError::InvalidRequest {
provider: "openai",
message: "Invalid parameters".to_string(),
};
let error = GatewayError::Provider(provider_error);
let response = error.error_response();
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
}
#[test]
fn test_provider_error_timeout_response() {
let provider_error = ProviderError::Timeout {
provider: "openai",
message: "Request timed out after 30s".to_string(),
};
let error = GatewayError::Provider(provider_error);
let response = error.error_response();
assert_eq!(response.status(), StatusCode::GATEWAY_TIMEOUT);
}
#[test]
fn test_provider_error_unavailable_response() {
let provider_error = ProviderError::ProviderUnavailable {
provider: "azure",
message: "Service maintenance".to_string(),
};
let error = GatewayError::Provider(provider_error);
let response = error.error_response();
assert_eq!(response.status(), StatusCode::SERVICE_UNAVAILABLE);
}
#[test]
fn test_provider_error_authentication_response() {
let provider_error = ProviderError::Authentication {
provider: "openai",
message: "Invalid API key".to_string(),
};
let error = GatewayError::Provider(provider_error);
let response = error.error_response();
assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
}
#[test]
fn test_provider_error_api_error_passthrough() {
let provider_error = ProviderError::ApiError {
provider: "unknown",
status: 418,
message: "I'm a teapot".to_string(),
};
let error = GatewayError::Provider(provider_error);
let response = error.error_response();
assert_eq!(response.status().as_u16(), 418);
}
#[test]
fn test_provider_error_api_error_invalid_status_fallback() {
let provider_error = ProviderError::ApiError {
provider: "unknown",
status: 0,
message: "Invalid status".to_string(),
};
let error = GatewayError::Provider(provider_error);
let response = error.error_response();
assert_eq!(response.status(), StatusCode::BAD_GATEWAY);
}
#[test]
fn test_provider_error_context_length_response() {
let provider_error = ProviderError::ContextLengthExceeded {
provider: "openai",
max: 8192,
actual: 10000,
};
let error = GatewayError::Provider(provider_error);
let response = error.error_response();
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
}
#[test]
fn test_provider_error_not_supported_response() {
let provider_error = ProviderError::NotSupported {
provider: "openai",
feature: "vision".to_string(),
};
let error = GatewayError::Provider(provider_error);
let response = error.error_response();
assert_eq!(response.status(), StatusCode::NOT_IMPLEMENTED);
}
#[test]
fn test_provider_error_configuration_response() {
let provider_error = ProviderError::Configuration {
provider: "azure",
message: "Missing deployment".to_string(),
};
let error = GatewayError::Provider(provider_error);
let response = error.error_response();
assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
}
#[test]
fn test_provider_error_deployment_response() {
let provider_error = ProviderError::DeploymentError {
provider: "azure",
deployment: "gpt-4-east".to_string(),
message: "Deployment not found".to_string(),
};
let error = GatewayError::Provider(provider_error);
let response = error.error_response();
assert_eq!(response.status(), StatusCode::NOT_FOUND);
}
#[test]
fn test_provider_error_routing_response() {
let provider_error = ProviderError::RoutingError {
provider: "openrouter",
attempted_providers: vec!["openai".to_string(), "anthropic".to_string()],
message: "All providers failed".to_string(),
};
let error = GatewayError::Provider(provider_error);
let response = error.error_response();
assert_eq!(response.status(), StatusCode::SERVICE_UNAVAILABLE);
}
#[test]
fn test_provider_error_other_variant_response() {
let provider_error = ProviderError::Other {
provider: "custom",
message: "Unknown error".to_string(),
};
let error = GatewayError::Provider(provider_error);
let response = error.error_response();
assert_eq!(response.status(), StatusCode::BAD_GATEWAY);
}
#[test]
fn test_gateway_error_jwt_response() {
use jsonwebtoken::{errors::Error as JwtError, errors::ErrorKind};
let jwt_error = JwtError::from(ErrorKind::InvalidToken);
let error = GatewayError::Auth(format!("JWT error: {}", jwt_error));
let response = error.error_response();
assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
}
#[test]
fn test_gateway_error_serialization_response() {
let json_err: serde_json::Error = serde_json::from_str::<serde_json::Value>("bad").unwrap_err();
let error: GatewayError = json_err.into();
let response = error.error_response();
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
}
#[test]
fn test_gateway_error_cache_response() {
let error = GatewayError::Storage("Cache unavailable".to_string());
let response = error.error_response();
assert_eq!(response.status(), StatusCode::SERVICE_UNAVAILABLE);
}
#[test]
fn test_gateway_error_crypto_response() {
let error = GatewayError::Auth("Encryption failed".to_string());
let response = error.error_response();
assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
}
#[test]
fn test_gateway_error_file_storage_response() {
let error = GatewayError::Internal("Write failed".to_string());
let response = error.error_response();
assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
}
#[test]
fn test_gateway_error_http_client_response() {
let req_err = reqwest::Client::new()
.get("not-a-valid-url")
.build()
.unwrap_err();
let error = GatewayError::HttpClient(req_err);
let response = error.error_response();
assert_eq!(response.status(), StatusCode::BAD_GATEWAY);
}
#[test]
fn test_gateway_error_io_response() {
let io_err = std::io::Error::new(std::io::ErrorKind::PermissionDenied, "permission denied");
let error = GatewayError::Io(io_err);
let response = error.error_response();
assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
}
#[test]
fn test_gateway_error_vector_db_response() {
let error = GatewayError::Storage("Vector search failed".to_string());
let response = error.error_response();
assert_eq!(response.status(), StatusCode::SERVICE_UNAVAILABLE);
}
#[test]
fn test_gateway_error_yaml_response() {
let yaml_err: serde_yml::Error =
serde_yml::from_str::<serde_yml::Value>("key: [unclosed").unwrap_err();
let error = GatewayError::Serialization(yaml_err.to_string());
let response = error.error_response();
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
}
#[cfg(feature = "vector-db")]
#[test]
fn test_gateway_error_qdrant_response() {
let error = GatewayError::Storage("Qdrant unavailable".to_string());
let response = error.error_response();
assert_eq!(response.status(), StatusCode::SERVICE_UNAVAILABLE);
}
#[cfg(feature = "websockets")]
#[test]
fn test_gateway_error_websocket_response() {
let error = GatewayError::Network("WS connection failed".to_string());
let response = error.error_response();
assert_eq!(response.status(), StatusCode::BAD_GATEWAY);
}
#[cfg(feature = "s3")]
#[test]
fn test_gateway_error_s3_response() {
let error = GatewayError::Storage("Bucket not found".to_string());
let response = error.error_response();
assert_eq!(response.status(), StatusCode::SERVICE_UNAVAILABLE);
}
#[test]
fn test_error_response_json_structure() {
let error = GatewayError::Auth("Invalid credentials".to_string());
let response = error.error_response();
assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
}
#[test]
fn test_error_detail_timestamp_is_current() {
let before = chrono::Utc::now().timestamp();
let detail = GatewayErrorDetail {
code: "TEST".to_string(),
canonical_code: "INTERNAL".to_string(),
retryable: false,
message: "Test".to_string(),
timestamp: chrono::Utc::now().timestamp(),
request_id: None,
};
let after = chrono::Utc::now().timestamp();
assert!(detail.timestamp >= before);
assert!(detail.timestamp <= after);
}
#[test]
fn test_multiple_error_codes() {
let error_codes = vec![
("CONFIG_ERROR", GatewayError::Config("test".to_string())),
("AUTH_ERROR", GatewayError::Auth("test".to_string())),
(
"VALIDATION_ERROR",
GatewayError::Validation("test".to_string()),
),
("NOT_FOUND", GatewayError::NotFound("test".to_string())),
("RATE_LIMIT_EXCEEDED", GatewayError::rate_limit("test")),
];
for (_expected_code, error) in error_codes {
let response = error.error_response();
assert!(response.status().is_client_error() || response.status().is_server_error());
}
}