1use anyhow::Result;
2use std::collections::HashMap;
4use std::fmt;
5use thiserror::Error;
6
7#[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#[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#[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 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 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 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 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 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 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 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 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 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 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 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 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 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 pub fn add_error_mapping(&mut self, pattern: PythonErrorPattern, mapping: LambdaErrorMapping) {
713 self.error_mappings.insert(pattern, mapping);
714 }
715
716 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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[test]
1269 fn test_lambda_error_handler_new() {
1270 let handler = LambdaErrorHandler::new();
1271 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 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 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 #[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 #[test]
1417 fn test_generate_error_enum_content() {
1418 let handler = LambdaErrorHandler::new();
1419 let code = handler.generate_error_handling_code().unwrap();
1420
1421 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 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 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 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 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 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 assert!(retry.contains("retry_with_backoff"));
1503 assert!(retry.contains("tokio::time::sleep"));
1504 }
1505
1506 #[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}