use crate::error::{ErrorCategory, ErrorSeverity, LlmError};
#[cfg(test)]
mod llm_error_categorization_tests {
use super::*;
#[test]
fn test_unsupported_provider_error_categorization() {
let provider_name = "unsupported-provider";
let error = LlmError::unsupported_provider(provider_name);
assert_eq!(error.category(), ErrorCategory::Client);
assert_eq!(error.severity(), ErrorSeverity::Error);
assert!(
!error.is_retryable(),
"Configuration errors should not be retryable"
);
}
#[test]
fn test_configuration_error_categorization() {
let error_message = "Missing API key";
let error = LlmError::configuration_error(error_message);
assert_eq!(error.category(), ErrorCategory::Client);
assert_eq!(error.severity(), ErrorSeverity::Error);
assert!(
!error.is_retryable(),
"Configuration errors should not be retryable"
);
}
#[test]
fn test_request_failed_error_categorization() {
let error_message = "HTTP request timeout";
let error = LlmError::request_failed(error_message, None);
assert_eq!(error.category(), ErrorCategory::External);
assert_eq!(error.severity(), ErrorSeverity::Error);
assert!(
error.is_retryable(),
"External request failures should be retryable"
);
}
#[test]
fn test_rate_limit_exceeded_categorization() {
let retry_after_seconds = 60;
let error = LlmError::rate_limit_exceeded(retry_after_seconds);
assert_eq!(error.category(), ErrorCategory::Transient);
assert_eq!(error.severity(), ErrorSeverity::Warning);
assert!(
error.is_retryable(),
"Rate limit errors should be retryable"
);
}
#[test]
fn test_timeout_error_categorization() {
let timeout_seconds = 30;
let error = LlmError::timeout(timeout_seconds);
assert_eq!(error.category(), ErrorCategory::Transient);
assert_eq!(error.severity(), ErrorSeverity::Warning);
assert!(error.is_retryable(), "Timeout errors should be retryable");
}
#[test]
fn test_authentication_failed_categorization() {
let auth_message = "Invalid API key";
let error = LlmError::authentication_failed(auth_message);
assert_eq!(error.category(), ErrorCategory::Client);
assert_eq!(error.severity(), ErrorSeverity::Error);
assert!(
!error.is_retryable(),
"Authentication errors should not be retryable"
);
}
#[test]
fn test_token_limit_exceeded_categorization() {
let current_tokens = 5000;
let max_tokens = 4000;
let error = LlmError::token_limit_exceeded(current_tokens, max_tokens);
assert_eq!(error.category(), ErrorCategory::Client);
assert_eq!(error.severity(), ErrorSeverity::Info);
assert!(
!error.is_retryable(),
"Token limit errors require user input changes"
);
}
}
#[cfg(test)]
mod llm_error_user_messages_tests {
use super::*;
#[test]
fn test_unsupported_provider_user_message() {
let provider_name = "internal-test-provider";
let error = LlmError::unsupported_provider(provider_name);
let user_message = error.user_message();
assert_eq!(user_message, "The requested AI provider is not supported");
assert!(
!user_message.contains("internal-test-provider"),
"User message should not expose internal provider names"
);
}
#[test]
fn test_configuration_error_user_message() {
let technical_message = "ANTHROPIC_API_KEY environment variable not set";
let error = LlmError::configuration_error(technical_message);
let user_message = error.user_message();
assert_eq!(
user_message,
"AI service configuration issue. Please check your settings"
);
assert!(
!user_message.contains("ANTHROPIC_API_KEY"),
"User message should not expose technical configuration details"
);
}
#[test]
fn test_rate_limit_exceeded_user_message_includes_wait_time() {
let retry_after_seconds = 120;
let error = LlmError::rate_limit_exceeded(retry_after_seconds);
let user_message = error.user_message();
assert_eq!(
user_message,
"Service is busy. Please wait 120 seconds and try again"
);
assert!(
user_message.contains("120"),
"Should include specific wait time"
);
}
#[test]
fn test_authentication_failed_user_message() {
let technical_message = "API key 'sk-abc123...' is invalid or expired";
let error = LlmError::authentication_failed(technical_message);
let user_message = error.user_message();
assert_eq!(
user_message,
"Authentication failed. Please check your credentials"
);
assert!(
!user_message.contains("sk-abc123"),
"User message should not expose API key details"
);
}
#[test]
fn test_token_limit_exceeded_user_message() {
let current_tokens = 5000;
let max_tokens = 4000;
let error = LlmError::token_limit_exceeded(current_tokens, max_tokens);
let user_message = error.user_message();
assert_eq!(
user_message,
"Your request is too long. Please shorten it and try again"
);
assert!(
!user_message.contains("5000"),
"User message should not expose technical token counts"
);
}
}
#[cfg(test)]
mod llm_error_constructor_tests {
use super::*;
#[test]
fn test_response_parsing_error_creation() {
let parsing_message = "Invalid JSON response format";
let error = LlmError::response_parsing_error(parsing_message);
assert_eq!(error.category(), ErrorCategory::External);
assert_eq!(error.severity(), ErrorSeverity::Warning);
assert!(
!error.is_retryable(),
"Parsing errors typically require different handling"
);
assert!(error.to_string().contains("Invalid JSON response format"));
}
#[test]
fn test_tool_execution_failed_creation() {
let tool_name = "web_search";
let error_message = "Network timeout during search";
let error = LlmError::tool_execution_failed(tool_name, error_message);
assert_eq!(error.category(), ErrorCategory::External);
assert_eq!(error.severity(), ErrorSeverity::Error);
assert!(
!error.is_retryable(),
"Tool execution failures require specific handling"
);
let error_string = error.to_string();
assert!(error_string.contains("web_search"));
assert!(error_string.contains("Network timeout during search"));
}
#[test]
fn test_schema_validation_failed_creation() {
let validation_message = "Required field 'title' missing from response";
let error = LlmError::schema_validation_failed(validation_message);
assert_eq!(error.category(), ErrorCategory::Client);
assert_eq!(error.severity(), ErrorSeverity::Warning);
assert!(
!error.is_retryable(),
"Schema validation requires schema or prompt fixes"
);
assert!(error.to_string().contains("Required field 'title' missing"));
}
#[test]
fn test_request_failed_with_source_error() {
let request_message = "HTTP request failed";
let source_error = Box::new(std::io::Error::new(
std::io::ErrorKind::TimedOut,
"Connection timeout",
));
let error = LlmError::request_failed(request_message, Some(source_error));
assert_eq!(error.category(), ErrorCategory::External);
assert_eq!(error.severity(), ErrorSeverity::Error);
assert!(
error.is_retryable(),
"Network request failures should be retryable"
);
assert!(error.to_string().contains("HTTP request failed"));
assert!(
std::error::Error::source(&error).is_some(),
"Source error should be preserved"
);
}
}
#[cfg(test)]
mod llm_error_display_tests {
use super::*;
#[test]
fn test_error_display_format_consistency() {
let provider = "test-provider";
let timeout_seconds = 45;
let current_tokens = 1500;
let max_tokens = 1000;
let unsupported_error = LlmError::unsupported_provider(provider);
let timeout_error = LlmError::timeout(timeout_seconds);
let token_error = LlmError::token_limit_exceeded(current_tokens, max_tokens);
assert_eq!(
unsupported_error.to_string(),
"Provider not supported: test-provider"
);
assert_eq!(timeout_error.to_string(), "Request timed out after 45s");
assert_eq!(token_error.to_string(), "Token limit exceeded: 1500 > 1000");
}
#[test]
fn test_error_debug_representation() {
let error = LlmError::configuration_error("Test configuration issue");
let debug_string = format!("{:?}", error);
assert!(debug_string.contains("ConfigurationError"));
assert!(debug_string.contains("Test configuration issue"));
}
}