Skip to main content

depyler_lambda/
lambda_errors.rs

1use anyhow::Result;
2// use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4use std::fmt;
5use thiserror::Error;
6
7/// Lambda-specific error handling pipeline with automatic conversion from Python errors
8#[derive(Debug, Clone)]
9pub struct LambdaErrorHandler {
10    error_mappings: HashMap<PythonErrorPattern, LambdaErrorMapping>,
11    error_strategy: ErrorHandlingStrategy,
12}
13
14#[derive(Debug, Clone, PartialEq, Eq, Hash)]
15pub struct PythonErrorPattern {
16    pub error_type: String,
17    pub message_pattern: Option<String>,
18    pub context: Option<ErrorContext>,
19}
20
21#[derive(Debug, Clone)]
22pub struct LambdaErrorMapping {
23    pub rust_error_type: String,
24    pub status_code: Option<u16>,
25    pub error_message_template: String,
26    pub include_stack_trace: bool,
27    pub retry_strategy: RetryStrategy,
28}
29
30#[derive(Debug, Clone, PartialEq, Eq, Hash)]
31pub enum ErrorContext {
32    Handler,
33    Serialization,
34    EventProcessing,
35    ResponseGeneration,
36    Initialization,
37}
38
39#[derive(Debug, Clone, PartialEq, Default)]
40pub enum ErrorHandlingStrategy {
41    Panic,
42    #[default]
43    ReturnError,
44    LogAndContinue,
45    CustomHandler(String),
46}
47
48#[derive(Debug, Clone, PartialEq)]
49pub enum RetryStrategy {
50    None,
51    Immediate,
52    ExponentialBackoff,
53    Custom(String),
54}
55
56/// Lambda runtime errors that can occur during transpilation and execution
57#[derive(Error, Debug)]
58pub enum LambdaError {
59    #[error("Serialization failed: {message}")]
60    Serialization {
61        message: String,
62        cause: Option<Box<dyn std::error::Error + Send + Sync>>,
63    },
64
65    #[error("Handler error: {message}")]
66    Handler {
67        message: String,
68        context: Option<String>,
69    },
70
71    #[error("Runtime error: {0}")]
72    Runtime(String),
73
74    #[error("HTTP error: {status} - {message}")]
75    Http { status: u16, message: String },
76
77    #[error("Missing parameter: {parameter}")]
78    MissingParameter { parameter: String },
79
80    #[error("Invalid event format: {message}")]
81    InvalidEvent {
82        message: String,
83        event_type: Option<String>,
84    },
85
86    #[error("Authentication failed: {message}")]
87    Authentication { message: String },
88
89    #[error("Authorization failed: {message}")]
90    Authorization { message: String },
91
92    #[error("Timeout occurred: {operation} took {duration_ms}ms")]
93    Timeout { operation: String, duration_ms: u64 },
94
95    #[error("Resource limit exceeded: {resource} - {limit}")]
96    ResourceLimit { resource: String, limit: String },
97
98    #[error("Configuration error: {message}")]
99    Configuration { message: String },
100
101    #[error("External service error: {service} - {message}")]
102    ExternalService { service: String, message: String },
103}
104
105impl LambdaError {
106    pub fn status_code(&self) -> u16 {
107        match self {
108            LambdaError::MissingParameter { .. } => 400,
109            LambdaError::Handler { .. } => 400,
110            LambdaError::InvalidEvent { .. } => 400,
111            LambdaError::Authentication { .. } => 401,
112            LambdaError::Authorization { .. } => 403,
113            LambdaError::Timeout { .. } => 504,
114            LambdaError::ExternalService { .. } => 502,
115            LambdaError::Http { status, .. } => *status,
116            _ => 500,
117        }
118    }
119
120    pub fn should_retry(&self) -> bool {
121        match self {
122            LambdaError::Timeout { .. } => true,
123            LambdaError::ExternalService { .. } => true,
124            LambdaError::Http { status, .. } => *status >= 500,
125            _ => false,
126        }
127    }
128}
129
130/// Generated error conversion code for common Python error patterns
131#[derive(Debug, Clone)]
132pub struct ErrorConversionCode {
133    pub conversion_functions: String,
134    pub error_enum: String,
135    pub helper_traits: String,
136}
137
138impl Default for LambdaErrorHandler {
139    fn default() -> Self {
140        Self::new()
141    }
142}
143
144impl LambdaErrorHandler {
145    pub fn new() -> Self {
146        let mut error_mappings = HashMap::new();
147
148        // Python KeyError mappings
149        error_mappings.insert(
150            PythonErrorPattern {
151                error_type: "KeyError".to_string(),
152                message_pattern: None,
153                context: Some(ErrorContext::EventProcessing),
154            },
155            LambdaErrorMapping {
156                rust_error_type: "LambdaError::MissingParameter".to_string(),
157                status_code: Some(400),
158                error_message_template: "Missing required parameter: {parameter}".to_string(),
159                include_stack_trace: false,
160                retry_strategy: RetryStrategy::None,
161            },
162        );
163
164        // Python ValueError mappings
165        error_mappings.insert(
166            PythonErrorPattern {
167                error_type: "ValueError".to_string(),
168                message_pattern: None,
169                context: Some(ErrorContext::Handler),
170            },
171            LambdaErrorMapping {
172                rust_error_type: "LambdaError::Handler".to_string(),
173                status_code: Some(400),
174                error_message_template: "Invalid value: {message}".to_string(),
175                include_stack_trace: false,
176                retry_strategy: RetryStrategy::None,
177            },
178        );
179
180        // Python TypeError mappings
181        error_mappings.insert(
182            PythonErrorPattern {
183                error_type: "TypeError".to_string(),
184                message_pattern: None,
185                context: Some(ErrorContext::Serialization),
186            },
187            LambdaErrorMapping {
188                rust_error_type: "LambdaError::Serialization".to_string(),
189                status_code: Some(500),
190                error_message_template: "Type conversion error: {message}".to_string(),
191                include_stack_trace: true,
192                retry_strategy: RetryStrategy::None,
193            },
194        );
195
196        // JSON decode errors
197        error_mappings.insert(
198            PythonErrorPattern {
199                error_type: "json.JSONDecodeError".to_string(),
200                message_pattern: None,
201                context: Some(ErrorContext::Serialization),
202            },
203            LambdaErrorMapping {
204                rust_error_type: "LambdaError::Serialization".to_string(),
205                status_code: Some(400),
206                error_message_template: "Invalid JSON: {message}".to_string(),
207                include_stack_trace: false,
208                retry_strategy: RetryStrategy::None,
209            },
210        );
211
212        // HTTP-related errors
213        error_mappings.insert(
214            PythonErrorPattern {
215                error_type: "requests.HTTPError".to_string(),
216                message_pattern: None,
217                context: Some(ErrorContext::Handler),
218            },
219            LambdaErrorMapping {
220                rust_error_type: "LambdaError::ExternalService".to_string(),
221                status_code: Some(502),
222                error_message_template: "External service error: {message}".to_string(),
223                include_stack_trace: false,
224                retry_strategy: RetryStrategy::ExponentialBackoff,
225            },
226        );
227
228        // Timeout errors
229        error_mappings.insert(
230            PythonErrorPattern {
231                error_type: "TimeoutError".to_string(),
232                message_pattern: None,
233                context: Some(ErrorContext::Handler),
234            },
235            LambdaErrorMapping {
236                rust_error_type: "LambdaError::Timeout".to_string(),
237                status_code: Some(504),
238                error_message_template: "Operation timed out: {message}".to_string(),
239                include_stack_trace: false,
240                retry_strategy: RetryStrategy::Immediate,
241            },
242        );
243
244        Self {
245            error_mappings,
246            error_strategy: ErrorHandlingStrategy::default(),
247        }
248    }
249
250    pub fn with_strategy(mut self, strategy: ErrorHandlingStrategy) -> Self {
251        self.error_strategy = strategy;
252        self
253    }
254
255    /// Generate error handling code for Lambda functions
256    pub fn generate_error_handling_code(&self) -> Result<ErrorConversionCode> {
257        let conversion_functions = self.generate_conversion_functions();
258        let error_enum = self.generate_error_enum();
259        let helper_traits = self.generate_helper_traits();
260
261        Ok(ErrorConversionCode {
262            conversion_functions,
263            error_enum,
264            helper_traits,
265        })
266    }
267
268    /// Generate Rust error enum definition
269    fn generate_error_enum(&self) -> String {
270        r#"#[derive(Debug, thiserror::Error)]
271pub enum LambdaError {
272    #[error("Serialization failed: {message}")]
273    Serialization {
274        message: String,
275        #[source]
276        cause: Option<Box<dyn std::error::Error + Send + Sync>>,
277    },
278    
279    #[error("Handler error: {message}")]
280    Handler {
281        message: String,
282        context: Option<String>,
283    },
284    
285    #[error("Runtime error: {0}")]
286    Runtime(#[from] lambda_runtime::Error),
287    
288    #[error("HTTP error: {status} - {message}")]
289    Http {
290        status: u16,
291        message: String,
292    },
293    
294    #[error("Missing parameter: {parameter}")]
295    MissingParameter {
296        parameter: String,
297    },
298    
299    #[error("Invalid event format: {message}")]
300    InvalidEvent {
301        message: String,
302        event_type: Option<String>,
303    },
304    
305    #[error("Authentication failed: {message}")]
306    Authentication {
307        message: String,
308    },
309    
310    #[error("Authorization failed: {message}")]
311    Authorization {
312        message: String,
313    },
314    
315    #[error("Timeout occurred: {operation} took {duration_ms}ms")]
316    Timeout {
317        operation: String,
318        duration_ms: u64,
319    },
320    
321    #[error("Resource limit exceeded: {resource} - {limit}")]
322    ResourceLimit {
323        resource: String,
324        limit: String,
325    },
326    
327    #[error("Configuration error: {message}")]
328    Configuration {
329        message: String,
330    },
331    
332    #[error("External service error: {service} - {message}")]
333    ExternalService {
334        service: String,
335        message: String,
336    },
337}
338
339impl LambdaError {
340    pub fn status_code(&self) -> u16 {
341        match self {
342            LambdaError::MissingParameter { .. } => 400,
343            LambdaError::Handler { .. } => 400,
344            LambdaError::InvalidEvent { .. } => 400,
345            LambdaError::Authentication { .. } => 401,
346            LambdaError::Authorization { .. } => 403,
347            LambdaError::Timeout { .. } => 504,
348            LambdaError::ExternalService { .. } => 502,
349            LambdaError::Http { status, .. } => *status,
350            _ => 500,
351        }
352    }
353    
354    pub fn should_retry(&self) -> bool {
355        match self {
356            LambdaError::Timeout { .. } => true,
357            LambdaError::ExternalService { .. } => true,
358            LambdaError::Http { status, .. } => *status >= 500,
359            _ => false,
360        }
361    }
362}
363"#
364        .to_string()
365    }
366
367    /// Generate conversion functions from Python error patterns
368    fn generate_conversion_functions(&self) -> String {
369        let mut functions = String::new();
370
371        functions.push_str(
372            r#"// Automatic error conversion functions
373impl From<serde_json::Error> for LambdaError {{
374    fn from(err: serde_json::Error) -> Self {{
375        LambdaError::Serialization {{
376            message: err.to_string(),
377            cause: Some(Box::new(err)),
378        }}
379    }}
380}}
381
382impl From<&str> for LambdaError {{
383    fn from(msg: &str) -> Self {{
384        // Pattern matching on common Python error messages
385        if msg.contains("KeyError") {{
386            let parameter = extract_key_error_parameter(msg).unwrap_or_else(|| "unknown".to_string());
387            LambdaError::MissingParameter {{ parameter }}
388        }} else if msg.contains("ValueError") {{
389            LambdaError::Handler {{
390                message: msg.to_string(),
391                context: Some("ValueError".to_string()),
392            }}
393        }} else if msg.contains("TypeError") {{
394            LambdaError::Serialization {{
395                message: msg.to_string(),
396                cause: None,
397            }}
398        }} else if msg.contains("TimeoutError") {{
399            LambdaError::Timeout {{
400                operation: "unknown".to_string(),
401                duration_ms: 0,
402            }}
403        }} else {{
404            LambdaError::Handler {{
405                message: msg.to_string(),
406                context: None,
407            }}
408        }}
409    }}
410}}
411
412fn extract_key_error_parameter(error_msg: &str) -> Option<String> {{
413    // Extract parameter name from KeyError messages like "KeyError: 'param_name'"
414    if let Some(start) = error_msg.find("'") {{
415        if let Some(end) = error_msg[start + 1..].find("'") {{
416            return Some(error_msg[start + 1..start + 1 + end].to_string());
417        }}
418    }}
419    None
420}}
421
422"#
423        );
424
425        // Generate API Gateway specific error handling
426        functions.push_str(
427            r#"// API Gateway specific error handling
428impl From<LambdaError> for aws_lambda_events::apigw::ApiGatewayProxyResponse {{
429    fn from(err: LambdaError) -> Self {{
430        let status_code = err.status_code();
431        let error_body = serde_json::json!({{
432            "error": {{
433                "message": err.to_string(),
434                "type": match &err {{
435                    LambdaError::MissingParameter {{ .. }} => "MissingParameter",
436                    LambdaError::Handler {{ .. }} => "HandlerError",
437                    LambdaError::Serialization {{ .. }} => "SerializationError",
438                    LambdaError::Timeout {{ .. }} => "TimeoutError",
439                    _ => "InternalError",
440                }},
441                "retryable": err.should_retry(),
442            }}
443        }});
444        
445        let mut headers = std::collections::HashMap::new();
446        headers.insert("Content-Type".to_string(), "application/json".to_string());
447        
448        aws_lambda_events::apigw::ApiGatewayProxyResponse {{
449            status_code,
450            headers,
451            multi_value_headers: std::collections::HashMap::new(),
452            body: Some(error_body.to_string()),
453            is_base64_encoded: false,
454        }}
455    }}
456}}
457
458// API Gateway v2 specific error handling
459impl From<LambdaError> for aws_lambda_events::apigw::ApiGatewayV2httpResponse {{
460    fn from(err: LambdaError) -> Self {{
461        let status_code = err.status_code();
462        let error_body = serde_json::json!({{
463            "error": {{
464                "message": err.to_string(),
465                "type": match &err {{
466                    LambdaError::MissingParameter {{ .. }} => "MissingParameter",
467                    LambdaError::Handler {{ .. }} => "HandlerError",
468                    LambdaError::Serialization {{ .. }} => "SerializationError",
469                    LambdaError::Timeout {{ .. }} => "TimeoutError",
470                    _ => "InternalError",
471                }},
472                "retryable": err.should_retry(),
473            }}
474        }});
475        
476        let mut headers = std::collections::HashMap::new();
477        headers.insert("Content-Type".to_string(), "application/json".to_string());
478        
479        aws_lambda_events::apigw::ApiGatewayV2httpResponse {{
480            status_code,
481            headers,
482            body: Some(error_body.to_string()),
483            is_base64_encoded: Some(false),
484            cookies: vec![],
485        }}
486    }}
487}}
488
489"#,
490        );
491
492        functions
493    }
494
495    /// Generate helper traits for error handling
496    fn generate_helper_traits(&self) -> String {
497        r#"// Helper traits for error handling
498pub trait LambdaErrorExt {
499    fn with_context(self, context: &str) -> LambdaError;
500    fn with_parameter(self, parameter: &str) -> LambdaError;
501    fn with_status(self, status: u16) -> LambdaError;
502}
503
504impl LambdaErrorExt for String {{
505    fn with_context(self, context: &str) -> LambdaError {{
506        LambdaError::Handler {{
507            message: self,
508            context: Some(context.to_string()),
509        }}
510    }}
511    
512    fn with_parameter(self, parameter: &str) -> LambdaError {{
513        LambdaError::MissingParameter {{
514            parameter: parameter.to_string(),
515        }}
516    }}
517    
518    fn with_status(self, status: u16) -> LambdaError {{
519        LambdaError::Http {{
520            status,
521            message: self,
522        }}
523    }}
524}}
525
526impl LambdaErrorExt for &str {{
527    fn with_context(self, context: &str) -> LambdaError {{
528        self.to_string().with_context(context)
529    }}
530    
531    fn with_parameter(self, parameter: &str) -> LambdaError {{
532        self.to_string().with_parameter(parameter)
533    }}
534    
535    fn with_status(self, status: u16) -> LambdaError {{
536        self.to_string().with_status(status)
537    }}
538}}
539
540// Result type alias for Lambda functions
541pub type LambdaResult<T> = std::result::Result<T, LambdaError>;
542
543// Macro for easy error creation
544#[macro_export]
545macro_rules! lambda_error {{
546    ($msg:expr) => {{
547        LambdaError::Handler {{
548            message: $msg.to_string(),
549            context: None,
550        }}
551    }};
552    ($msg:expr, $context:expr) => {{
553        LambdaError::Handler {{
554            message: $msg.to_string(),
555            context: Some($context.to_string()),
556        }}
557    }};
558}}
559
560// Macro for parameter validation
561#[macro_export]
562macro_rules! require_param {{
563    ($event:expr, $key:expr) => {{
564        $event.get($key).ok_or_else(|| {{
565            LambdaError::MissingParameter {{
566                parameter: $key.to_string(),
567            }}
568        }})
569    }};
570}}
571
572"#
573        .to_string()
574    }
575
576    /// Generate error handling wrapper for handler functions
577    pub fn generate_handler_wrapper(&self, handler_name: &str) -> String {
578        match &self.error_strategy {
579            ErrorHandlingStrategy::ReturnError => {
580                format!(
581                    r#"// Error handling wrapper for {handler_name}
582async fn {handler_name}_with_error_handling(
583    event: LambdaEvent<serde_json::Value>
584) -> Result<serde_json::Value, LambdaError> {{
585    match {handler_name}(event).await {{
586        Ok(response) => Ok(response),
587        Err(err) => {{
588            // Log the error for debugging
589            eprintln!("Handler error: {{:?}}", err);
590            
591            // Return appropriate error response
592            Err(err.into())
593        }}
594    }}
595}}
596"#
597                )
598            }
599            ErrorHandlingStrategy::LogAndContinue => {
600                format!(
601                    r#"// Error handling wrapper for {handler_name} (log and continue)
602async fn {handler_name}_with_error_handling(
603    event: LambdaEvent<serde_json::Value>
604) -> Result<serde_json::Value, LambdaError> {{
605    match {handler_name}(event).await {{
606        Ok(response) => Ok(response),
607        Err(err) => {{
608            // Log the error
609            eprintln!("Handler error (continuing): {{:?}}", err);
610            
611            // Return default response
612            Ok(serde_json::json!({{
613                "status": "error_logged",
614                "message": "An error occurred but was handled"
615            }}))
616        }}
617    }}
618}}
619"#
620                )
621            }
622            ErrorHandlingStrategy::Panic => {
623                format!(
624                    r#"// Error handling wrapper for {handler_name} (panic on error)
625async fn {handler_name}_with_error_handling(
626    event: LambdaEvent<serde_json::Value>
627) -> Result<serde_json::Value, LambdaError> {{
628    match {handler_name}(event).await {{
629        Ok(response) => Ok(response),
630        Err(err) => {{
631            eprintln!("Handler error (panicking): {{:?}}", err);
632            panic!("Handler failed: {{}}", err);
633        }}
634    }}
635}}
636"#
637                )
638            }
639            ErrorHandlingStrategy::CustomHandler(custom_code) => {
640                format!(
641                    r#"// Custom error handling wrapper for {handler_name}
642async fn {handler_name}_with_error_handling(
643    event: LambdaEvent<serde_json::Value>
644) -> Result<serde_json::Value, LambdaError> {{
645    match {handler_name}(event).await {{
646        Ok(response) => Ok(response),
647        Err(err) => {{
648            {custom_code}
649        }}
650    }}
651}}
652"#
653                )
654            }
655        }
656    }
657
658    /// Generate retry logic for Lambda functions
659    pub fn generate_retry_logic(&self) -> String {
660        r#"// Retry logic for Lambda functions
661pub struct RetryConfig {{
662    pub max_attempts: u32,
663    pub base_delay_ms: u64,
664    pub max_delay_ms: u64,
665    pub backoff_multiplier: f64,
666}}
667
668impl Default for RetryConfig {{
669    fn default() -> Self {{
670        Self {{
671            max_attempts: 3,
672            base_delay_ms: 100,
673            max_delay_ms: 5000,
674            backoff_multiplier: 2.0,
675        }}
676    }}
677}}
678
679pub async fn retry_with_backoff<F, T, E>(
680    config: &RetryConfig,
681    mut operation: F,
682) -> Result<T, E>
683where
684    F: FnMut() -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<T, E>> + Send>>,
685    E: std::fmt::Debug,
686{{
687    let mut last_error = None;
688    let mut delay = config.base_delay_ms;
689    
690    for attempt in 1..=config.max_attempts {{
691        match operation().await {{
692            Ok(result) => return Ok(result),
693            Err(err) => {{
694                eprintln!("Attempt {{}} failed: {{:?}}", attempt, err);
695                last_error = Some(err);
696                
697                if attempt < config.max_attempts {{
698                    tokio::time::sleep(tokio::time::Duration::from_millis(delay)).await;
699                    delay = ((delay as f64 * config.backoff_multiplier) as u64).min(config.max_delay_ms);
700                }}
701            }}
702        }}
703    }}
704    
705    Err(last_error.unwrap())
706}}
707
708"#.to_string()
709    }
710
711    /// Add custom error mapping
712    pub fn add_error_mapping(&mut self, pattern: PythonErrorPattern, mapping: LambdaErrorMapping) {
713        self.error_mappings.insert(pattern, mapping);
714    }
715
716    /// Get error mapping for a Python error pattern
717    pub fn get_error_mapping(&self, pattern: &PythonErrorPattern) -> Option<&LambdaErrorMapping> {
718        self.error_mappings.get(pattern)
719    }
720}
721
722impl fmt::Display for PythonErrorPattern {
723    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
724        write!(f, "{}", self.error_type)?;
725        if let Some(ref message) = self.message_pattern {
726            write!(f, " ({message})")?;
727        }
728        if let Some(ref context) = self.context {
729            write!(f, " in {context:?}")?;
730        }
731        Ok(())
732    }
733}
734
735#[cfg(test)]
736mod tests {
737    use super::*;
738
739    // === PythonErrorPattern tests ===
740
741    #[test]
742    fn test_python_error_pattern_fields() {
743        let pattern = PythonErrorPattern {
744            error_type: "ValueError".to_string(),
745            message_pattern: Some("invalid input".to_string()),
746            context: Some(ErrorContext::Handler),
747        };
748        assert_eq!(pattern.error_type, "ValueError");
749        assert_eq!(pattern.message_pattern, Some("invalid input".to_string()));
750        assert_eq!(pattern.context, Some(ErrorContext::Handler));
751    }
752
753    #[test]
754    fn test_python_error_pattern_clone() {
755        let pattern = PythonErrorPattern {
756            error_type: "KeyError".to_string(),
757            message_pattern: None,
758            context: None,
759        };
760        let cloned = pattern.clone();
761        assert_eq!(cloned.error_type, "KeyError");
762    }
763
764    #[test]
765    fn test_python_error_pattern_debug() {
766        let pattern = PythonErrorPattern {
767            error_type: "TypeError".to_string(),
768            message_pattern: None,
769            context: None,
770        };
771        let debug = format!("{:?}", pattern);
772        assert!(debug.contains("PythonErrorPattern"));
773        assert!(debug.contains("TypeError"));
774    }
775
776    #[test]
777    fn test_python_error_pattern_eq() {
778        let p1 = PythonErrorPattern {
779            error_type: "KeyError".to_string(),
780            message_pattern: None,
781            context: Some(ErrorContext::Handler),
782        };
783        let p2 = PythonErrorPattern {
784            error_type: "KeyError".to_string(),
785            message_pattern: None,
786            context: Some(ErrorContext::Handler),
787        };
788        assert_eq!(p1, p2);
789    }
790
791    #[test]
792    fn test_python_error_pattern_hash() {
793        use std::collections::HashSet;
794        let mut set = HashSet::new();
795        let p1 = PythonErrorPattern {
796            error_type: "ValueError".to_string(),
797            message_pattern: None,
798            context: None,
799        };
800        set.insert(p1.clone());
801        assert!(set.contains(&p1));
802    }
803
804    #[test]
805    fn test_python_error_pattern_display() {
806        let pattern = PythonErrorPattern {
807            error_type: "KeyError".to_string(),
808            message_pattern: Some("missing key".to_string()),
809            context: Some(ErrorContext::EventProcessing),
810        };
811        let display = format!("{}", pattern);
812        assert!(display.contains("KeyError"));
813        assert!(display.contains("missing key"));
814        assert!(display.contains("EventProcessing"));
815    }
816
817    #[test]
818    fn test_python_error_pattern_display_no_optionals() {
819        let pattern = PythonErrorPattern {
820            error_type: "RuntimeError".to_string(),
821            message_pattern: None,
822            context: None,
823        };
824        let display = format!("{}", pattern);
825        assert_eq!(display, "RuntimeError");
826    }
827
828    // === LambdaErrorMapping tests ===
829
830    #[test]
831    fn test_lambda_error_mapping_fields() {
832        let mapping = LambdaErrorMapping {
833            rust_error_type: "LambdaError::Handler".to_string(),
834            status_code: Some(400),
835            error_message_template: "Error: {message}".to_string(),
836            include_stack_trace: true,
837            retry_strategy: RetryStrategy::None,
838        };
839        assert_eq!(mapping.rust_error_type, "LambdaError::Handler");
840        assert_eq!(mapping.status_code, Some(400));
841        assert!(mapping.include_stack_trace);
842    }
843
844    #[test]
845    fn test_lambda_error_mapping_clone() {
846        let mapping = LambdaErrorMapping {
847            rust_error_type: "LambdaError::Timeout".to_string(),
848            status_code: Some(504),
849            error_message_template: "Timeout".to_string(),
850            include_stack_trace: false,
851            retry_strategy: RetryStrategy::Immediate,
852        };
853        let cloned = mapping.clone();
854        assert_eq!(cloned.status_code, Some(504));
855    }
856
857    #[test]
858    fn test_lambda_error_mapping_debug() {
859        let mapping = LambdaErrorMapping {
860            rust_error_type: "LambdaError::Runtime".to_string(),
861            status_code: None,
862            error_message_template: "Runtime error".to_string(),
863            include_stack_trace: false,
864            retry_strategy: RetryStrategy::ExponentialBackoff,
865        };
866        let debug = format!("{:?}", mapping);
867        assert!(debug.contains("LambdaErrorMapping"));
868    }
869
870    // === ErrorContext tests ===
871
872    #[test]
873    fn test_error_context_variants() {
874        assert_eq!(ErrorContext::Handler, ErrorContext::Handler);
875        assert_eq!(ErrorContext::Serialization, ErrorContext::Serialization);
876        assert_eq!(ErrorContext::EventProcessing, ErrorContext::EventProcessing);
877        assert_eq!(
878            ErrorContext::ResponseGeneration,
879            ErrorContext::ResponseGeneration
880        );
881        assert_eq!(ErrorContext::Initialization, ErrorContext::Initialization);
882    }
883
884    #[test]
885    fn test_error_context_clone() {
886        let ctx = ErrorContext::Handler;
887        let cloned = ctx.clone();
888        assert_eq!(cloned, ErrorContext::Handler);
889    }
890
891    #[test]
892    fn test_error_context_debug() {
893        let ctx = ErrorContext::Serialization;
894        let debug = format!("{:?}", ctx);
895        assert!(debug.contains("Serialization"));
896    }
897
898    #[test]
899    fn test_error_context_hash() {
900        use std::collections::HashSet;
901        let mut set = HashSet::new();
902        set.insert(ErrorContext::Handler);
903        set.insert(ErrorContext::Serialization);
904        assert_eq!(set.len(), 2);
905        assert!(set.contains(&ErrorContext::Handler));
906    }
907
908    #[test]
909    fn test_error_context_ne() {
910        assert_ne!(ErrorContext::Handler, ErrorContext::Serialization);
911    }
912
913    // === ErrorHandlingStrategy tests ===
914
915    #[test]
916    fn test_error_handling_strategy_variants() {
917        let strategies = [
918            ErrorHandlingStrategy::Panic,
919            ErrorHandlingStrategy::ReturnError,
920            ErrorHandlingStrategy::LogAndContinue,
921            ErrorHandlingStrategy::CustomHandler("custom".to_string()),
922        ];
923        assert_eq!(strategies.len(), 4);
924    }
925
926    #[test]
927    fn test_error_handling_strategy_default() {
928        let default = ErrorHandlingStrategy::default();
929        assert_eq!(default, ErrorHandlingStrategy::ReturnError);
930    }
931
932    #[test]
933    fn test_error_handling_strategy_clone() {
934        let strategy = ErrorHandlingStrategy::Panic;
935        let cloned = strategy.clone();
936        assert_eq!(cloned, ErrorHandlingStrategy::Panic);
937    }
938
939    #[test]
940    fn test_error_handling_strategy_debug() {
941        let strategy = ErrorHandlingStrategy::LogAndContinue;
942        let debug = format!("{:?}", strategy);
943        assert!(debug.contains("LogAndContinue"));
944    }
945
946    #[test]
947    fn test_error_handling_strategy_eq() {
948        assert_eq!(ErrorHandlingStrategy::Panic, ErrorHandlingStrategy::Panic);
949        assert_ne!(
950            ErrorHandlingStrategy::Panic,
951            ErrorHandlingStrategy::ReturnError
952        );
953    }
954
955    #[test]
956    fn test_error_handling_strategy_custom_handler() {
957        let custom = ErrorHandlingStrategy::CustomHandler("my_handler()".to_string());
958        if let ErrorHandlingStrategy::CustomHandler(code) = custom {
959            assert_eq!(code, "my_handler()");
960        } else {
961            panic!("Expected CustomHandler");
962        }
963    }
964
965    // === RetryStrategy tests ===
966
967    #[test]
968    fn test_retry_strategy_variants() {
969        let strategies = [
970            RetryStrategy::None,
971            RetryStrategy::Immediate,
972            RetryStrategy::ExponentialBackoff,
973            RetryStrategy::Custom("custom".to_string()),
974        ];
975        assert_eq!(strategies.len(), 4);
976    }
977
978    #[test]
979    fn test_retry_strategy_clone() {
980        let strategy = RetryStrategy::ExponentialBackoff;
981        let cloned = strategy.clone();
982        assert_eq!(cloned, RetryStrategy::ExponentialBackoff);
983    }
984
985    #[test]
986    fn test_retry_strategy_debug() {
987        let strategy = RetryStrategy::Immediate;
988        let debug = format!("{:?}", strategy);
989        assert!(debug.contains("Immediate"));
990    }
991
992    #[test]
993    fn test_retry_strategy_eq() {
994        assert_eq!(RetryStrategy::None, RetryStrategy::None);
995        assert_ne!(RetryStrategy::None, RetryStrategy::Immediate);
996    }
997
998    #[test]
999    fn test_retry_strategy_custom() {
1000        let custom = RetryStrategy::Custom("retry_with_jitter".to_string());
1001        if let RetryStrategy::Custom(code) = custom {
1002            assert_eq!(code, "retry_with_jitter");
1003        } else {
1004            panic!("Expected Custom");
1005        }
1006    }
1007
1008    // === LambdaError tests ===
1009
1010    #[test]
1011    fn test_lambda_error_serialization() {
1012        let err = LambdaError::Serialization {
1013            message: "JSON parse failed".to_string(),
1014            cause: None,
1015        };
1016        assert_eq!(err.status_code(), 500);
1017        assert!(!err.should_retry());
1018    }
1019
1020    #[test]
1021    fn test_lambda_error_handler() {
1022        let err = LambdaError::Handler {
1023            message: "Handler failed".to_string(),
1024            context: Some("validation".to_string()),
1025        };
1026        assert_eq!(err.status_code(), 400);
1027        assert!(!err.should_retry());
1028    }
1029
1030    #[test]
1031    fn test_lambda_error_runtime() {
1032        let err = LambdaError::Runtime("runtime crashed".to_string());
1033        assert_eq!(err.status_code(), 500);
1034        assert!(!err.should_retry());
1035    }
1036
1037    #[test]
1038    fn test_lambda_error_http() {
1039        let err = LambdaError::Http {
1040            status: 404,
1041            message: "Not found".to_string(),
1042        };
1043        assert_eq!(err.status_code(), 404);
1044        assert!(!err.should_retry());
1045
1046        let err_500 = LambdaError::Http {
1047            status: 503,
1048            message: "Service unavailable".to_string(),
1049        };
1050        assert!(err_500.should_retry());
1051    }
1052
1053    #[test]
1054    fn test_lambda_error_invalid_event() {
1055        let err = LambdaError::InvalidEvent {
1056            message: "Invalid format".to_string(),
1057            event_type: Some("S3Event".to_string()),
1058        };
1059        assert_eq!(err.status_code(), 400);
1060        assert!(!err.should_retry());
1061    }
1062
1063    #[test]
1064    fn test_lambda_error_authentication() {
1065        let err = LambdaError::Authentication {
1066            message: "Invalid token".to_string(),
1067        };
1068        assert_eq!(err.status_code(), 401);
1069        assert!(!err.should_retry());
1070    }
1071
1072    #[test]
1073    fn test_lambda_error_authorization() {
1074        let err = LambdaError::Authorization {
1075            message: "Access denied".to_string(),
1076        };
1077        assert_eq!(err.status_code(), 403);
1078        assert!(!err.should_retry());
1079    }
1080
1081    #[test]
1082    fn test_lambda_error_resource_limit() {
1083        let err = LambdaError::ResourceLimit {
1084            resource: "memory".to_string(),
1085            limit: "128MB".to_string(),
1086        };
1087        assert_eq!(err.status_code(), 500);
1088        assert!(!err.should_retry());
1089    }
1090
1091    #[test]
1092    fn test_lambda_error_configuration() {
1093        let err = LambdaError::Configuration {
1094            message: "Missing env var".to_string(),
1095        };
1096        assert_eq!(err.status_code(), 500);
1097        assert!(!err.should_retry());
1098    }
1099
1100    #[test]
1101    fn test_lambda_error_external_service() {
1102        let err = LambdaError::ExternalService {
1103            service: "DynamoDB".to_string(),
1104            message: "Throttled".to_string(),
1105        };
1106        assert_eq!(err.status_code(), 502);
1107        assert!(err.should_retry());
1108    }
1109
1110    #[test]
1111    fn test_lambda_error_display_serialization() {
1112        let err = LambdaError::Serialization {
1113            message: "parse error".to_string(),
1114            cause: None,
1115        };
1116        let display = err.to_string();
1117        assert!(display.contains("Serialization failed"));
1118        assert!(display.contains("parse error"));
1119    }
1120
1121    #[test]
1122    fn test_lambda_error_display_handler() {
1123        let err = LambdaError::Handler {
1124            message: "invalid input".to_string(),
1125            context: None,
1126        };
1127        let display = err.to_string();
1128        assert!(display.contains("Handler error"));
1129    }
1130
1131    #[test]
1132    fn test_lambda_error_display_runtime() {
1133        let err = LambdaError::Runtime("panic occurred".to_string());
1134        let display = err.to_string();
1135        assert!(display.contains("Runtime error"));
1136        assert!(display.contains("panic occurred"));
1137    }
1138
1139    #[test]
1140    fn test_lambda_error_display_http() {
1141        let err = LambdaError::Http {
1142            status: 500,
1143            message: "Internal error".to_string(),
1144        };
1145        let display = err.to_string();
1146        assert!(display.contains("HTTP error"));
1147        assert!(display.contains("500"));
1148    }
1149
1150    #[test]
1151    fn test_lambda_error_display_missing_parameter() {
1152        let err = LambdaError::MissingParameter {
1153            parameter: "user_id".to_string(),
1154        };
1155        let display = err.to_string();
1156        assert!(display.contains("Missing parameter"));
1157        assert!(display.contains("user_id"));
1158    }
1159
1160    #[test]
1161    fn test_lambda_error_display_invalid_event() {
1162        let err = LambdaError::InvalidEvent {
1163            message: "bad format".to_string(),
1164            event_type: Some("SQS".to_string()),
1165        };
1166        let display = err.to_string();
1167        assert!(display.contains("Invalid event format"));
1168    }
1169
1170    #[test]
1171    fn test_lambda_error_display_authentication() {
1172        let err = LambdaError::Authentication {
1173            message: "expired token".to_string(),
1174        };
1175        let display = err.to_string();
1176        assert!(display.contains("Authentication failed"));
1177    }
1178
1179    #[test]
1180    fn test_lambda_error_display_authorization() {
1181        let err = LambdaError::Authorization {
1182            message: "insufficient permissions".to_string(),
1183        };
1184        let display = err.to_string();
1185        assert!(display.contains("Authorization failed"));
1186    }
1187
1188    #[test]
1189    fn test_lambda_error_display_timeout() {
1190        let err = LambdaError::Timeout {
1191            operation: "db_query".to_string(),
1192            duration_ms: 30000,
1193        };
1194        let display = err.to_string();
1195        assert!(display.contains("Timeout"));
1196        assert!(display.contains("db_query"));
1197        assert!(display.contains("30000"));
1198    }
1199
1200    #[test]
1201    fn test_lambda_error_display_resource_limit() {
1202        let err = LambdaError::ResourceLimit {
1203            resource: "CPU".to_string(),
1204            limit: "100%".to_string(),
1205        };
1206        let display = err.to_string();
1207        assert!(display.contains("Resource limit exceeded"));
1208    }
1209
1210    #[test]
1211    fn test_lambda_error_display_configuration() {
1212        let err = LambdaError::Configuration {
1213            message: "invalid config".to_string(),
1214        };
1215        let display = err.to_string();
1216        assert!(display.contains("Configuration error"));
1217    }
1218
1219    #[test]
1220    fn test_lambda_error_display_external_service() {
1221        let err = LambdaError::ExternalService {
1222            service: "S3".to_string(),
1223            message: "bucket not found".to_string(),
1224        };
1225        let display = err.to_string();
1226        assert!(display.contains("External service error"));
1227        assert!(display.contains("S3"));
1228    }
1229
1230    // === ErrorConversionCode tests ===
1231
1232    #[test]
1233    fn test_error_conversion_code_fields() {
1234        let code = ErrorConversionCode {
1235            conversion_functions: "fn convert()".to_string(),
1236            error_enum: "enum Error {}".to_string(),
1237            helper_traits: "trait Helper {}".to_string(),
1238        };
1239        assert!(code.conversion_functions.contains("convert"));
1240        assert!(code.error_enum.contains("enum"));
1241        assert!(code.helper_traits.contains("trait"));
1242    }
1243
1244    #[test]
1245    fn test_error_conversion_code_clone() {
1246        let code = ErrorConversionCode {
1247            conversion_functions: "fn a()".to_string(),
1248            error_enum: "enum E {}".to_string(),
1249            helper_traits: "trait T {}".to_string(),
1250        };
1251        let cloned = code.clone();
1252        assert_eq!(cloned.conversion_functions, "fn a()");
1253    }
1254
1255    #[test]
1256    fn test_error_conversion_code_debug() {
1257        let code = ErrorConversionCode {
1258            conversion_functions: "code".to_string(),
1259            error_enum: "enum".to_string(),
1260            helper_traits: "trait".to_string(),
1261        };
1262        let debug = format!("{:?}", code);
1263        assert!(debug.contains("ErrorConversionCode"));
1264    }
1265
1266    // === LambdaErrorHandler tests ===
1267
1268    #[test]
1269    fn test_lambda_error_handler_new() {
1270        let handler = LambdaErrorHandler::new();
1271        // Should have default error mappings
1272        let pattern = PythonErrorPattern {
1273            error_type: "KeyError".to_string(),
1274            message_pattern: None,
1275            context: Some(ErrorContext::EventProcessing),
1276        };
1277        assert!(handler.get_error_mapping(&pattern).is_some());
1278    }
1279
1280    #[test]
1281    fn test_lambda_error_handler_default() {
1282        let handler = LambdaErrorHandler::default();
1283        let code = handler.generate_error_handling_code().unwrap();
1284        assert!(!code.error_enum.is_empty());
1285    }
1286
1287    #[test]
1288    fn test_lambda_error_handler_clone() {
1289        let handler = LambdaErrorHandler::new();
1290        let cloned = handler.clone();
1291        let code = cloned.generate_error_handling_code().unwrap();
1292        assert!(code.error_enum.contains("LambdaError"));
1293    }
1294
1295    #[test]
1296    fn test_lambda_error_handler_debug() {
1297        let handler = LambdaErrorHandler::new();
1298        let debug = format!("{:?}", handler);
1299        assert!(debug.contains("LambdaErrorHandler"));
1300    }
1301
1302    #[test]
1303    fn test_lambda_error_handler_with_strategy() {
1304        let handler = LambdaErrorHandler::new().with_strategy(ErrorHandlingStrategy::Panic);
1305        let wrapper = handler.generate_handler_wrapper("test");
1306        assert!(wrapper.contains("panicking"));
1307    }
1308
1309    #[test]
1310    fn test_lambda_error_handler_get_mapping_not_found() {
1311        let handler = LambdaErrorHandler::new();
1312        let pattern = PythonErrorPattern {
1313            error_type: "UnknownError".to_string(),
1314            message_pattern: None,
1315            context: None,
1316        };
1317        assert!(handler.get_error_mapping(&pattern).is_none());
1318    }
1319
1320    #[test]
1321    fn test_lambda_error_handler_default_mappings() {
1322        let handler = LambdaErrorHandler::new();
1323
1324        // Test ValueError mapping
1325        let value_error = PythonErrorPattern {
1326            error_type: "ValueError".to_string(),
1327            message_pattern: None,
1328            context: Some(ErrorContext::Handler),
1329        };
1330        let mapping = handler.get_error_mapping(&value_error).unwrap();
1331        assert_eq!(mapping.status_code, Some(400));
1332
1333        // Test TypeError mapping
1334        let type_error = PythonErrorPattern {
1335            error_type: "TypeError".to_string(),
1336            message_pattern: None,
1337            context: Some(ErrorContext::Serialization),
1338        };
1339        let mapping = handler.get_error_mapping(&type_error).unwrap();
1340        assert_eq!(mapping.status_code, Some(500));
1341    }
1342
1343    #[test]
1344    fn test_lambda_error_handler_json_decode_error_mapping() {
1345        let handler = LambdaErrorHandler::new();
1346        let pattern = PythonErrorPattern {
1347            error_type: "json.JSONDecodeError".to_string(),
1348            message_pattern: None,
1349            context: Some(ErrorContext::Serialization),
1350        };
1351        let mapping = handler.get_error_mapping(&pattern).unwrap();
1352        assert_eq!(mapping.status_code, Some(400));
1353    }
1354
1355    #[test]
1356    fn test_lambda_error_handler_http_error_mapping() {
1357        let handler = LambdaErrorHandler::new();
1358        let pattern = PythonErrorPattern {
1359            error_type: "requests.HTTPError".to_string(),
1360            message_pattern: None,
1361            context: Some(ErrorContext::Handler),
1362        };
1363        let mapping = handler.get_error_mapping(&pattern).unwrap();
1364        assert_eq!(mapping.retry_strategy, RetryStrategy::ExponentialBackoff);
1365    }
1366
1367    #[test]
1368    fn test_lambda_error_handler_timeout_error_mapping() {
1369        let handler = LambdaErrorHandler::new();
1370        let pattern = PythonErrorPattern {
1371            error_type: "TimeoutError".to_string(),
1372            message_pattern: None,
1373            context: Some(ErrorContext::Handler),
1374        };
1375        let mapping = handler.get_error_mapping(&pattern).unwrap();
1376        assert_eq!(mapping.retry_strategy, RetryStrategy::Immediate);
1377    }
1378
1379    // === Handler wrapper generation tests ===
1380
1381    #[test]
1382    fn test_handler_wrapper_return_error() {
1383        let handler = LambdaErrorHandler::new().with_strategy(ErrorHandlingStrategy::ReturnError);
1384        let wrapper = handler.generate_handler_wrapper("my_func");
1385        assert!(wrapper.contains("my_func_with_error_handling"));
1386        assert!(wrapper.contains("Err(err.into())"));
1387    }
1388
1389    #[test]
1390    fn test_handler_wrapper_log_and_continue() {
1391        let handler =
1392            LambdaErrorHandler::new().with_strategy(ErrorHandlingStrategy::LogAndContinue);
1393        let wrapper = handler.generate_handler_wrapper("my_func");
1394        assert!(wrapper.contains("log and continue"));
1395        assert!(wrapper.contains("error_logged"));
1396    }
1397
1398    #[test]
1399    fn test_handler_wrapper_panic() {
1400        let handler = LambdaErrorHandler::new().with_strategy(ErrorHandlingStrategy::Panic);
1401        let wrapper = handler.generate_handler_wrapper("my_func");
1402        assert!(wrapper.contains("panic!"));
1403    }
1404
1405    #[test]
1406    fn test_handler_wrapper_custom() {
1407        let custom_code = "log_error(&err); return Err(err)".to_string();
1408        let handler = LambdaErrorHandler::new()
1409            .with_strategy(ErrorHandlingStrategy::CustomHandler(custom_code.clone()));
1410        let wrapper = handler.generate_handler_wrapper("my_func");
1411        assert!(wrapper.contains(&custom_code));
1412    }
1413
1414    // === Code generation tests ===
1415
1416    #[test]
1417    fn test_generate_error_enum_content() {
1418        let handler = LambdaErrorHandler::new();
1419        let code = handler.generate_error_handling_code().unwrap();
1420
1421        // Check all error variants are present
1422        assert!(code.error_enum.contains("Serialization"));
1423        assert!(code.error_enum.contains("Handler"));
1424        assert!(code.error_enum.contains("Runtime"));
1425        assert!(code.error_enum.contains("Http"));
1426        assert!(code.error_enum.contains("MissingParameter"));
1427        assert!(code.error_enum.contains("InvalidEvent"));
1428        assert!(code.error_enum.contains("Authentication"));
1429        assert!(code.error_enum.contains("Authorization"));
1430        assert!(code.error_enum.contains("Timeout"));
1431        assert!(code.error_enum.contains("ResourceLimit"));
1432        assert!(code.error_enum.contains("Configuration"));
1433        assert!(code.error_enum.contains("ExternalService"));
1434    }
1435
1436    #[test]
1437    fn test_generate_conversion_functions_content() {
1438        let handler = LambdaErrorHandler::new();
1439        let code = handler.generate_error_handling_code().unwrap();
1440
1441        // Check From implementations
1442        assert!(code
1443            .conversion_functions
1444            .contains("From<serde_json::Error>"));
1445        assert!(code.conversion_functions.contains("From<&str>"));
1446        assert!(code.conversion_functions.contains("KeyError"));
1447        assert!(code.conversion_functions.contains("ValueError"));
1448        assert!(code.conversion_functions.contains("TypeError"));
1449        assert!(code.conversion_functions.contains("TimeoutError"));
1450    }
1451
1452    #[test]
1453    fn test_generate_conversion_functions_api_gateway() {
1454        let handler = LambdaErrorHandler::new();
1455        let code = handler.generate_error_handling_code().unwrap();
1456
1457        // Check API Gateway conversions
1458        assert!(code
1459            .conversion_functions
1460            .contains("ApiGatewayProxyResponse"));
1461        assert!(code
1462            .conversion_functions
1463            .contains("ApiGatewayV2httpResponse"));
1464    }
1465
1466    #[test]
1467    fn test_generate_helper_traits_content() {
1468        let handler = LambdaErrorHandler::new();
1469        let code = handler.generate_error_handling_code().unwrap();
1470
1471        // Check helper traits
1472        assert!(code.helper_traits.contains("LambdaErrorExt"));
1473        assert!(code.helper_traits.contains("with_context"));
1474        assert!(code.helper_traits.contains("with_parameter"));
1475        assert!(code.helper_traits.contains("with_status"));
1476        assert!(code.helper_traits.contains("LambdaResult"));
1477    }
1478
1479    #[test]
1480    fn test_generate_helper_traits_macros() {
1481        let handler = LambdaErrorHandler::new();
1482        let code = handler.generate_error_handling_code().unwrap();
1483
1484        // Check macros
1485        assert!(code.helper_traits.contains("macro_rules! lambda_error"));
1486        assert!(code.helper_traits.contains("macro_rules! require_param"));
1487    }
1488
1489    #[test]
1490    fn test_generate_retry_logic_content() {
1491        let handler = LambdaErrorHandler::new();
1492        let retry = handler.generate_retry_logic();
1493
1494        // Check retry config
1495        assert!(retry.contains("RetryConfig"));
1496        assert!(retry.contains("max_attempts"));
1497        assert!(retry.contains("base_delay_ms"));
1498        assert!(retry.contains("max_delay_ms"));
1499        assert!(retry.contains("backoff_multiplier"));
1500
1501        // Check retry function
1502        assert!(retry.contains("retry_with_backoff"));
1503        assert!(retry.contains("tokio::time::sleep"));
1504    }
1505
1506    // === Original tests ===
1507
1508    #[test]
1509    fn test_error_enum_generation() {
1510        let handler = LambdaErrorHandler::new();
1511        let code = handler.generate_error_handling_code().unwrap();
1512
1513        assert!(code.error_enum.contains("enum LambdaError"));
1514        assert!(code.error_enum.contains("MissingParameter"));
1515        assert!(code.error_enum.contains("status_code"));
1516    }
1517
1518    #[test]
1519    fn test_conversion_functions() {
1520        let handler = LambdaErrorHandler::new();
1521        let code = handler.generate_error_handling_code().unwrap();
1522
1523        assert!(code
1524            .conversion_functions
1525            .contains("impl From<serde_json::Error>"));
1526        assert!(code
1527            .conversion_functions
1528            .contains("extract_key_error_parameter"));
1529    }
1530
1531    #[test]
1532    fn test_helper_traits() {
1533        let handler = LambdaErrorHandler::new();
1534        let code = handler.generate_error_handling_code().unwrap();
1535
1536        assert!(code.helper_traits.contains("trait LambdaErrorExt"));
1537        assert!(code.helper_traits.contains("with_context"));
1538        assert!(code.helper_traits.contains("LambdaResult"));
1539    }
1540
1541    #[test]
1542    fn test_handler_wrapper_generation() {
1543        let handler = LambdaErrorHandler::new().with_strategy(ErrorHandlingStrategy::ReturnError);
1544        let wrapper = handler.generate_handler_wrapper("my_handler");
1545
1546        assert!(wrapper.contains("my_handler_with_error_handling"));
1547        assert!(wrapper.contains("match my_handler(event).await"));
1548    }
1549
1550    #[test]
1551    fn test_retry_logic_generation() {
1552        let handler = LambdaErrorHandler::new();
1553        let retry_code = handler.generate_retry_logic();
1554
1555        assert!(retry_code.contains("struct RetryConfig"));
1556        assert!(retry_code.contains("retry_with_backoff"));
1557        assert!(retry_code.contains("tokio::time::sleep"));
1558    }
1559
1560    #[test]
1561    fn test_custom_error_mapping() {
1562        let mut handler = LambdaErrorHandler::new();
1563
1564        let pattern = PythonErrorPattern {
1565            error_type: "CustomError".to_string(),
1566            message_pattern: Some("custom pattern".to_string()),
1567            context: Some(ErrorContext::Handler),
1568        };
1569
1570        let mapping = LambdaErrorMapping {
1571            rust_error_type: "LambdaError::Custom".to_string(),
1572            status_code: Some(422),
1573            error_message_template: "Custom error: {message}".to_string(),
1574            include_stack_trace: true,
1575            retry_strategy: RetryStrategy::Custom("custom_retry".to_string()),
1576        };
1577
1578        handler.add_error_mapping(pattern.clone(), mapping);
1579
1580        let retrieved = handler.get_error_mapping(&pattern).unwrap();
1581        assert_eq!(retrieved.status_code, Some(422));
1582    }
1583
1584    #[test]
1585    fn test_error_strategies() {
1586        let strategies = vec![
1587            ErrorHandlingStrategy::ReturnError,
1588            ErrorHandlingStrategy::LogAndContinue,
1589            ErrorHandlingStrategy::Panic,
1590            ErrorHandlingStrategy::CustomHandler("custom".to_string()),
1591        ];
1592
1593        for strategy in strategies {
1594            let handler = LambdaErrorHandler::new().with_strategy(strategy);
1595            let wrapper = handler.generate_handler_wrapper("test_handler");
1596            assert!(wrapper.contains("test_handler_with_error_handling"));
1597        }
1598    }
1599
1600    #[test]
1601    fn test_lambda_error_methods() {
1602        let err = LambdaError::MissingParameter {
1603            parameter: "test_param".to_string(),
1604        };
1605
1606        assert_eq!(err.status_code(), 400);
1607        assert!(!err.should_retry());
1608
1609        let timeout_err = LambdaError::Timeout {
1610            operation: "test_op".to_string(),
1611            duration_ms: 5000,
1612        };
1613
1614        assert_eq!(timeout_err.status_code(), 504);
1615        assert!(timeout_err.should_retry());
1616    }
1617}