#[ cfg( feature = "retry" ) ]
mod retry_execution_tests
{
use crate::enhanced_retry_helpers::*;
use api_openai::error::OpenAIError;
use core::time::Duration;
use std::time::Instant;
#[ tokio::test ]
async fn test_retry_executor_successful_operation()
{
let config = EnhancedRetryConfig::new().with_max_attempts( 3 );
let executor = EnhancedRetryExecutor::new( config ).unwrap();
let result = executor.execute( || async { Ok( "success" ) } ).await;
assert!( result.is_ok() );
assert_eq!( result.unwrap(), "success" );
let state = executor.get_state();
assert_eq!( state.attempt, 1 );
assert_eq!( state.total_attempts, 1 );
}
#[ tokio::test ]
async fn test_retry_executor_with_transient_failures()
{
let config = EnhancedRetryConfig::new()
.with_max_attempts( 3 )
.with_base_delay( 10 ) .with_jitter( 0 );
let executor = EnhancedRetryExecutor::new( config ).unwrap();
let mock_client = MockHttpClient::new( vec![
Err( OpenAIError::Network( "Connection failed".to_string() ).into() ),
Err( OpenAIError::Http( "HTTP error with status 500: Server error".to_string() ).into() ),
Ok( "success".to_string() ),
] );
let result = executor.execute( || mock_client.make_request() ).await;
assert!( result.is_ok() );
assert_eq!( result.unwrap(), "success" );
let state = executor.get_state();
assert_eq!( state.attempt, 3 );
assert_eq!( state.total_attempts, 3 );
assert_eq!( mock_client.get_call_count(), 3 );
}
#[ tokio::test ]
async fn test_retry_executor_exceeds_max_attempts()
{
let config = EnhancedRetryConfig::new()
.with_max_attempts( 2 )
.with_base_delay( 10 )
.with_jitter( 0 );
let executor = EnhancedRetryExecutor::new( config ).unwrap();
let mock_client = MockHttpClient::new( vec![
Err( OpenAIError::Network( "Connection failed".to_string() ).into() ),
Err( OpenAIError::Network( "Connection failed".to_string() ).into() ),
Err( OpenAIError::Network( "Connection failed".to_string() ).into() ),
] );
let result = executor.execute( || mock_client.make_request() ).await;
assert!( result.is_err() );
let state = executor.get_state();
assert_eq!( state.attempt, 2 );
assert_eq!( state.total_attempts, 2 );
assert_eq!( mock_client.get_call_count(), 2 );
}
#[ tokio::test ]
async fn test_retry_executor_non_retryable_error()
{
let config = EnhancedRetryConfig::new().with_max_attempts( 3 );
let executor = EnhancedRetryExecutor::new( config ).unwrap();
let mock_client = MockHttpClient::new( vec![
Err( OpenAIError::InvalidArgument( "Invalid API key".to_string() ).into() ),
] );
let result = executor.execute( || mock_client.make_request() ).await;
assert!( result.is_err() );
let state = executor.get_state();
assert_eq!( state.attempt, 1 );
assert_eq!( state.total_attempts, 1 );
assert_eq!( mock_client.get_call_count(), 1 );
}
#[ tokio::test ]
async fn test_retry_executor_max_elapsed_time()
{
let config = EnhancedRetryConfig::new()
.with_max_attempts( 10 )
.with_base_delay( 200 ) .with_max_elapsed_time( 250 ) .with_jitter( 0 );
let executor = EnhancedRetryExecutor::new( config ).unwrap();
let mock_client = MockHttpClient::new( vec![
Err( OpenAIError::Network( "Connection failed".to_string() ).into() ),
Err( OpenAIError::Network( "Connection failed".to_string() ).into() ),
Err( OpenAIError::Network( "Connection failed".to_string() ).into() ),
Err( OpenAIError::Network( "Connection failed".to_string() ).into() ),
Err( OpenAIError::Network( "Connection failed".to_string() ).into() ),
Err( OpenAIError::Network( "Connection failed".to_string() ).into() ),
Err( OpenAIError::Network( "Connection failed".to_string() ).into() ),
Err( OpenAIError::Network( "Connection failed".to_string() ).into() ),
Err( OpenAIError::Network( "Connection failed".to_string() ).into() ),
Err( OpenAIError::Network( "Connection failed".to_string() ).into() ),
] );
let start_time = Instant::now();
let result = executor.execute( || mock_client.make_request() ).await;
let elapsed = start_time.elapsed();
assert!( result.is_err() );
let error_msg = result.unwrap_err().to_string();
assert!( error_msg.contains( "Max elapsed time exceeded" ), "Expected timeout error, got : {error_msg}" );
assert!( elapsed >= Duration::from_millis( 250 ) );
let state = executor.get_state();
assert!( state.attempt < 10 );
}
}