Skip to main content

depyler_lambda/
lambda_optimizer.rs

1use anyhow::Result;
2use depyler_annotations::{Architecture, LambdaAnnotations, LambdaEventType};
3use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5
6/// Cold start optimization and pre-warming strategies for Lambda functions
7#[derive(Debug, Clone)]
8pub struct LambdaOptimizer {
9    strategies: HashMap<OptimizationStrategy, OptimizationConfig>,
10    performance_targets: PerformanceTargets,
11}
12
13#[derive(Debug, Clone, PartialEq, Eq, Hash)]
14pub enum OptimizationStrategy {
15    BinarySize,
16    ColdStart,
17    PreWarming,
18    MemoryUsage,
19    CompileTime,
20}
21
22#[derive(Debug, Clone)]
23pub struct OptimizationConfig {
24    pub enabled: bool,
25    pub aggressive_mode: bool,
26    pub size_threshold_kb: Option<u32>,
27    pub cold_start_threshold_ms: Option<u32>,
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct PerformanceTargets {
32    pub max_cold_start_ms: u32,
33    pub max_binary_size_kb: u32,
34    pub max_memory_usage_mb: u16,
35    pub target_throughput_rps: Option<u32>,
36}
37
38#[derive(Debug, Clone)]
39pub struct OptimizationPlan {
40    pub profile_overrides: HashMap<String, String>,
41    pub cargo_flags: Vec<String>,
42    pub rustc_flags: Vec<String>,
43    pub pre_warm_code: String,
44    pub init_array_code: String,
45    pub dependency_optimizations: Vec<DependencyOptimization>,
46}
47
48#[derive(Debug, Clone)]
49pub struct DependencyOptimization {
50    pub crate_name: String,
51    pub features: Vec<String>,
52    pub disabled_features: Vec<String>,
53    pub replacement: Option<String>,
54}
55
56impl Default for PerformanceTargets {
57    fn default() -> Self {
58        Self {
59            max_cold_start_ms: 50,    // 50ms cold start target
60            max_binary_size_kb: 2048, // 2MB binary size target
61            max_memory_usage_mb: 128, // 128MB memory target
62            target_throughput_rps: None,
63        }
64    }
65}
66
67impl Default for OptimizationConfig {
68    fn default() -> Self {
69        Self {
70            enabled: true,
71            aggressive_mode: false,
72            size_threshold_kb: Some(1024),
73            cold_start_threshold_ms: Some(100),
74        }
75    }
76}
77
78impl Default for LambdaOptimizer {
79    fn default() -> Self {
80        Self::new()
81    }
82}
83
84impl LambdaOptimizer {
85    pub fn new() -> Self {
86        let mut strategies = HashMap::new();
87
88        strategies.insert(
89            OptimizationStrategy::BinarySize,
90            OptimizationConfig {
91                enabled: true,
92                aggressive_mode: true,
93                size_threshold_kb: Some(1024),
94                cold_start_threshold_ms: None,
95            },
96        );
97
98        strategies.insert(
99            OptimizationStrategy::ColdStart,
100            OptimizationConfig {
101                enabled: true,
102                aggressive_mode: true,
103                size_threshold_kb: None,
104                cold_start_threshold_ms: Some(50),
105            },
106        );
107
108        strategies.insert(
109            OptimizationStrategy::PreWarming,
110            OptimizationConfig {
111                enabled: true,
112                aggressive_mode: false,
113                size_threshold_kb: None,
114                cold_start_threshold_ms: None,
115            },
116        );
117
118        strategies.insert(
119            OptimizationStrategy::MemoryUsage,
120            OptimizationConfig {
121                enabled: true,
122                aggressive_mode: false,
123                size_threshold_kb: None,
124                cold_start_threshold_ms: None,
125            },
126        );
127
128        Self {
129            strategies,
130            performance_targets: PerformanceTargets::default(),
131        }
132    }
133
134    pub fn with_targets(mut self, targets: PerformanceTargets) -> Self {
135        self.performance_targets = targets;
136        self
137    }
138
139    pub fn enable_aggressive_mode(mut self) -> Self {
140        for config in self.strategies.values_mut() {
141            config.aggressive_mode = true;
142        }
143        self
144    }
145
146    /// Generate optimization plan based on Lambda annotations and event type
147    pub fn generate_optimization_plan(
148        &self,
149        annotations: &LambdaAnnotations,
150    ) -> Result<OptimizationPlan> {
151        let mut plan = OptimizationPlan {
152            profile_overrides: HashMap::new(),
153            cargo_flags: Vec::new(),
154            rustc_flags: Vec::new(),
155            pre_warm_code: String::new(),
156            init_array_code: String::new(),
157            dependency_optimizations: Vec::new(),
158        };
159
160        // Binary size optimizations
161        if self.is_strategy_enabled(&OptimizationStrategy::BinarySize) {
162            self.apply_binary_size_optimizations(&mut plan, annotations)?;
163        }
164
165        // Cold start optimizations
166        if self.is_strategy_enabled(&OptimizationStrategy::ColdStart)
167            && annotations.cold_start_optimize
168        {
169            self.apply_cold_start_optimizations(&mut plan, annotations)?;
170        }
171
172        // Pre-warming optimizations
173        if self.is_strategy_enabled(&OptimizationStrategy::PreWarming) {
174            self.apply_pre_warming_optimizations(&mut plan, annotations)?;
175        }
176
177        // Memory usage optimizations
178        if self.is_strategy_enabled(&OptimizationStrategy::MemoryUsage) {
179            self.apply_memory_optimizations(&mut plan, annotations)?;
180        }
181
182        Ok(plan)
183    }
184
185    fn apply_binary_size_optimizations(
186        &self,
187        plan: &mut OptimizationPlan,
188        annotations: &LambdaAnnotations,
189    ) -> Result<()> {
190        // Profile overrides for maximum size reduction
191        plan.profile_overrides
192            .insert("opt-level".to_string(), "z".to_string());
193        plan.profile_overrides
194            .insert("lto".to_string(), "true".to_string());
195        plan.profile_overrides
196            .insert("codegen-units".to_string(), "1".to_string());
197        plan.profile_overrides
198            .insert("panic".to_string(), "abort".to_string());
199        plan.profile_overrides
200            .insert("strip".to_string(), "true".to_string());
201        plan.profile_overrides
202            .insert("overflow-checks".to_string(), "false".to_string());
203        plan.profile_overrides
204            .insert("incremental".to_string(), "false".to_string());
205
206        // Aggressive RUSTC flags for size optimization
207        plan.rustc_flags.extend(vec![
208            "-C link-arg=-s".to_string(),      // Strip symbols
209            "-C opt-level=z".to_string(),      // Optimize for size
210            "-C codegen-units=1".to_string(),  // Single codegen unit
211            "-C lto=fat".to_string(),          // Fat LTO
212            "-C embed-bitcode=no".to_string(), // No bitcode embedding
213            "-C panic=abort".to_string(),      // Abort on panic
214        ]);
215
216        // Architecture-specific optimizations
217        match annotations.architecture {
218            Architecture::Arm64 => {
219                plan.rustc_flags
220                    .push("-C target-cpu=neoverse-n1".to_string());
221            }
222            Architecture::X86_64 => {
223                plan.rustc_flags.push("-C target-cpu=haswell".to_string());
224            }
225        }
226
227        // Dependency optimizations for size
228        plan.dependency_optimizations.extend(vec![
229            DependencyOptimization {
230                crate_name: "serde".to_string(),
231                features: vec!["derive".to_string()],
232                disabled_features: vec!["std".to_string()],
233                replacement: None,
234            },
235            DependencyOptimization {
236                crate_name: "tokio".to_string(),
237                features: vec!["rt".to_string(), "macros".to_string()],
238                disabled_features: vec!["full".to_string(), "test-util".to_string()],
239                replacement: None,
240            },
241        ]);
242
243        Ok(())
244    }
245
246    fn apply_cold_start_optimizations(
247        &self,
248        plan: &mut OptimizationPlan,
249        annotations: &LambdaAnnotations,
250    ) -> Result<()> {
251        // Pre-allocation and warming for common types
252        let mut pre_warm_code = String::new();
253
254        if let Some(ref event_type) = annotations.event_type {
255            match event_type {
256                LambdaEventType::ApiGatewayProxyRequest
257                | LambdaEventType::ApiGatewayV2HttpRequest => {
258                    pre_warm_code.push_str(
259                        r#"
260    // Pre-warm API Gateway types
261    let _ = std::hint::black_box(serde_json::from_str::<serde_json::Value>("{{}}"));
262    let _ = std::hint::black_box(std::collections::HashMap::<String, String>::new());
263    
264    // Pre-allocate response buffers
265    let mut response_buf = Vec::with_capacity(4096);
266    response_buf.push(0);
267    std::mem::forget(response_buf);
268"#,
269                    );
270                }
271                LambdaEventType::SqsEvent => {
272                    pre_warm_code.push_str(
273                        r#"
274    // Pre-warm SQS types
275    let _ = std::hint::black_box(Vec::<String>::with_capacity(10));
276    let _ = std::hint::black_box(String::with_capacity(1024));
277"#,
278                    );
279                }
280                LambdaEventType::S3Event => {
281                    pre_warm_code.push_str(
282                        r#"
283    // Pre-warm S3 types
284    let _ = std::hint::black_box(std::path::PathBuf::new());
285    let _ = std::hint::black_box(String::with_capacity(512));
286"#,
287                    );
288                }
289                _ => {}
290            }
291        }
292
293        // Global pre-warming
294        pre_warm_code.push_str(
295            r#"
296    // Pre-warm common allocations
297    let _ = std::hint::black_box(serde_json::Value::Null);
298    
299    // Initialize thread-local storage
300    thread_local! {{
301        static BUFFER: std::cell::RefCell<Vec<u8>> = std::cell::RefCell::new(Vec::with_capacity(8192));
302    }}
303    BUFFER.with(|_| {{}});
304"#
305        );
306
307        plan.pre_warm_code = pre_warm_code;
308
309        // Init array for early initialization
310        plan.init_array_code = r#"
311#[link_section = ".init_array"]
312static INIT: extern "C" fn() = {{
313    extern "C" fn init() {{
314        // Pre-warm critical allocators
315        let _ = std::hint::black_box(Vec::<u8>::with_capacity(1024));
316        let _ = std::hint::black_box(String::with_capacity(512));
317        
318        // Initialize mimalloc if enabled
319        #[cfg(feature = "mimalloc")]
320        {{
321            use mimalloc::MiMalloc;
322            let _ = std::hint::black_box(&MiMalloc);
323        }}
324    }}
325    init
326}};
327"#
328        .to_string();
329
330        // Configure opt-level=3 for latency-sensitive Lambda workloads (prioritizes response time)
331        plan.profile_overrides
332            .insert("opt-level".to_string(), "3".to_string());
333
334        Ok(())
335    }
336
337    fn apply_pre_warming_optimizations(
338        &self,
339        plan: &mut OptimizationPlan,
340        annotations: &LambdaAnnotations,
341    ) -> Result<()> {
342        // Event-specific pre-warming paths
343        for path in &annotations.pre_warm_paths {
344            plan.pre_warm_code.push_str(&format!(
345                "    // Pre-warm path: {path}\n    let _ = std::hint::black_box(String::from(\"{path}\"));\n"
346            ));
347        }
348
349        // Serde pre-warming for custom serialization
350        if annotations.custom_serialization {
351            plan.pre_warm_code.push_str(
352                r#"
353    // Pre-warm custom serialization paths
354    let _ = std::hint::black_box(serde_json::to_string(&serde_json::Value::Null));
355    let _ = std::hint::black_box(serde_json::from_str::<serde_json::Value>("null"));
356"#,
357            );
358        }
359
360        Ok(())
361    }
362
363    fn apply_memory_optimizations(
364        &self,
365        plan: &mut OptimizationPlan,
366        annotations: &LambdaAnnotations,
367    ) -> Result<()> {
368        // Use mimalloc for better memory allocation patterns
369        plan.dependency_optimizations.push(DependencyOptimization {
370            crate_name: "mimalloc".to_string(),
371            features: vec!["local_dynamic_tls".to_string()],
372            disabled_features: vec!["debug".to_string()],
373            replacement: None,
374        });
375
376        // Memory pool initialization for low-memory environments
377        if annotations.memory_size <= 128 {
378            plan.pre_warm_code.push_str(&format!(
379                r#"
380    // Memory-constrained optimization
381    if std::env::var("AWS_LAMBDA_FUNCTION_MEMORY_SIZE").unwrap_or_default() == "{}" {{
382        // Conservative pre-allocation for low memory
383        let _ = std::hint::black_box(Vec::<u8>::with_capacity(512));
384    }}
385"#,
386                annotations.memory_size
387            ));
388        }
389
390        // Stack size optimization
391        plan.rustc_flags
392            .push("-C link-arg=-Wl,-z,stack-size=131072".to_string()); // 128KB stack
393
394        Ok(())
395    }
396
397    /// Generate Cargo profile for Lambda optimization
398    pub fn generate_lambda_profile(&self, plan: &OptimizationPlan) -> String {
399        let mut profile = String::from("\n[profile.lambda]\ninherits = \"release\"\n");
400
401        for (key, value) in &plan.profile_overrides {
402            profile.push_str(&format!("{key} = {value}\n"));
403        }
404
405        // Add lambda-specific package overrides
406        profile.push_str("\n[profile.lambda.package.\"*\"]\n");
407        profile.push_str("opt-level = \"z\"\n");
408        profile.push_str("debug = false\n");
409
410        profile
411    }
412
413    /// Generate build script with optimization flags
414    pub fn generate_optimized_build_script(
415        &self,
416        plan: &OptimizationPlan,
417        annotations: &LambdaAnnotations,
418    ) -> String {
419        let mut script = format!(
420            r#"#!/bin/bash
421# Generated optimized build script for AWS Lambda
422
423set -e
424
425echo "Building optimized Lambda function..."
426echo "Target: {} MB memory, {} architecture"
427
428"#,
429            annotations.memory_size,
430            match annotations.architecture {
431                Architecture::Arm64 => "ARM64",
432                Architecture::X86_64 => "x86_64",
433            }
434        );
435
436        // Set environment variables
437        script.push_str("# Optimization environment variables\n");
438        script.push_str(&format!(
439            "export RUSTFLAGS=\"{}\"\n",
440            plan.rustc_flags.join(" ")
441        ));
442        script.push_str("export CARGO_PROFILE_LAMBDA_LTO=true\n");
443        script.push_str("export CARGO_PROFILE_LAMBDA_PANIC=\"abort\"\n");
444        script.push_str("export CARGO_PROFILE_LAMBDA_CODEGEN_UNITS=1\n");
445
446        // Build command
447        let arch_flag = match annotations.architecture {
448            Architecture::Arm64 => "--arm64",
449            Architecture::X86_64 => "--x86-64",
450        };
451
452        script.push_str(&format!(
453            r#"
454# Build with cargo-lambda
455cargo lambda build \
456    --profile lambda \
457    {arch_flag} \
458    --output-format zip
459
460"#
461        ));
462
463        // Post-build optimizations
464        script.push_str(
465            r#"
466# Post-build optimizations
467BINARY_PATH="target/lambda/*/bootstrap"
468
469if command -v strip > /dev/null; then
470    echo "Stripping binary..."
471    strip $BINARY_PATH
472fi
473
474if command -v upx > /dev/null; then
475    echo "Compressing binary with UPX..."
476    upx --best --lzma $BINARY_PATH || echo "UPX compression failed, continuing..."
477fi
478
479# Size reporting
480BINARY_SIZE=$(du -h $BINARY_PATH | cut -f1)
481echo "Final binary size: $BINARY_SIZE"
482
483# Cold start benchmark (if available)
484if command -v hyperfine > /dev/null; then
485    echo "Running cold start benchmark..."
486    # This would require a test harness
487    echo "Benchmark skipped - implement with your test harness"
488fi
489
490echo "Build completed successfully!"
491"#,
492        );
493
494        script
495    }
496
497    /// Generate performance monitoring code
498    pub fn generate_performance_monitoring(&self, _annotations: &LambdaAnnotations) -> String {
499        format!(
500            r#"
501use std::time::Instant;
502
503#[cfg(feature = "performance-monitoring")]
504mod performance {{
505    use super::*;
506    
507    pub struct PerformanceMonitor {{
508        start_time: Instant,
509        cold_start: bool,
510    }}
511    
512    impl PerformanceMonitor {{
513        pub fn new() -> Self {{
514            Self {{
515                start_time: Instant::now(),
516                cold_start: std::env::var("_LAMBDA_START_TIME").is_err(),
517            }}
518        }}
519        
520        pub fn log_cold_start(&self) {{
521            if self.cold_start {{
522                let duration = self.start_time.elapsed();
523                eprintln!("MONITORING cold_start_duration_ms:{{}}", duration.as_millis());
524                
525                if duration.as_millis() > {} {{
526                    eprintln!("WARNING: Cold start exceeded target of {}ms", {});
527                }}
528            }}
529        }}
530        
531        pub fn log_memory_usage(&self) {{
532            // This would require a memory profiling crate
533            if let Ok(memory_info) = std::fs::read_to_string("/proc/self/status") {{
534                for line in memory_info.lines() {{
535                    if line.starts_with("VmRSS:") {{
536                        eprintln!("MONITORING memory_usage:{{}}", line);
537                        break;
538                    }}
539                }}
540            }}
541        }}
542    }}
543}}
544"#,
545            self.performance_targets.max_cold_start_ms,
546            self.performance_targets.max_cold_start_ms,
547            self.performance_targets.max_cold_start_ms,
548        )
549    }
550
551    /// Check if optimization strategy is enabled
552    fn is_strategy_enabled(&self, strategy: &OptimizationStrategy) -> bool {
553        self.strategies
554            .get(strategy)
555            .is_some_and(|config| config.enabled)
556    }
557
558    /// Estimate performance impact of optimizations
559    pub fn estimate_performance_impact(&self, plan: &OptimizationPlan) -> PerformanceEstimate {
560        let mut estimate = PerformanceEstimate::default();
561
562        // Binary size reduction estimation
563        if plan.profile_overrides.contains_key("opt-level") {
564            estimate.binary_size_reduction_percent += 25.0;
565        }
566        if plan.rustc_flags.iter().any(|f| f.contains("lto=fat")) {
567            estimate.binary_size_reduction_percent += 15.0;
568        }
569        if plan.rustc_flags.iter().any(|f| f.contains("strip")) {
570            estimate.binary_size_reduction_percent += 30.0;
571        }
572
573        // Cold start improvement estimation
574        if !plan.pre_warm_code.is_empty() {
575            estimate.cold_start_improvement_percent += 40.0;
576        }
577        if !plan.init_array_code.is_empty() {
578            estimate.cold_start_improvement_percent += 20.0;
579        }
580
581        // Memory usage improvement
582        if plan
583            .dependency_optimizations
584            .iter()
585            .any(|d| d.crate_name == "mimalloc")
586        {
587            estimate.memory_improvement_percent += 15.0;
588        }
589
590        estimate
591    }
592}
593
594#[derive(Debug, Clone, Default)]
595pub struct PerformanceEstimate {
596    pub binary_size_reduction_percent: f32,
597    pub cold_start_improvement_percent: f32,
598    pub memory_improvement_percent: f32,
599    pub compile_time_impact_percent: f32,
600}
601
602#[cfg(test)]
603mod tests {
604    use super::*;
605    use depyler_annotations::LambdaAnnotations;
606
607    #[test]
608    fn test_optimization_plan_generation() {
609        let optimizer = LambdaOptimizer::new();
610        let annotations = LambdaAnnotations {
611            cold_start_optimize: true,
612            event_type: Some(LambdaEventType::ApiGatewayProxyRequest),
613            ..Default::default()
614        };
615
616        let plan = optimizer.generate_optimization_plan(&annotations).unwrap();
617
618        assert!(!plan.profile_overrides.is_empty());
619        assert!(!plan.rustc_flags.is_empty());
620        assert!(!plan.pre_warm_code.is_empty());
621    }
622
623    #[test]
624    fn test_binary_size_optimizations() {
625        let mut optimizer = LambdaOptimizer::new();
626        // Disable cold start optimization to test only binary size
627        optimizer
628            .strategies
629            .get_mut(&OptimizationStrategy::ColdStart)
630            .unwrap()
631            .enabled = false;
632        let annotations = LambdaAnnotations::default();
633
634        let plan = optimizer.generate_optimization_plan(&annotations).unwrap();
635
636        assert!(plan.profile_overrides.get("opt-level").unwrap() == "z");
637        assert!(plan.profile_overrides.get("lto").unwrap() == "true");
638        assert!(plan.rustc_flags.iter().any(|f| f.contains("link-arg=-s")));
639    }
640
641    #[test]
642    fn test_cold_start_optimizations() {
643        let optimizer = LambdaOptimizer::new();
644        let annotations = LambdaAnnotations {
645            cold_start_optimize: true,
646            event_type: Some(LambdaEventType::SqsEvent),
647            ..Default::default()
648        };
649
650        let plan = optimizer.generate_optimization_plan(&annotations).unwrap();
651
652        assert!(plan.pre_warm_code.contains("Pre-warm SQS types"));
653        assert!(!plan.init_array_code.is_empty());
654    }
655
656    #[test]
657    fn test_memory_optimizations() {
658        let optimizer = LambdaOptimizer::new();
659        let annotations = LambdaAnnotations {
660            memory_size: 128,
661            ..Default::default()
662        };
663
664        let plan = optimizer.generate_optimization_plan(&annotations).unwrap();
665
666        assert!(plan
667            .dependency_optimizations
668            .iter()
669            .any(|d| d.crate_name == "mimalloc"));
670        assert!(plan
671            .pre_warm_code
672            .contains("Memory-constrained optimization"));
673    }
674
675    #[test]
676    fn test_lambda_profile_generation() {
677        let optimizer = LambdaOptimizer::new();
678        let annotations = LambdaAnnotations::default();
679        let plan = optimizer.generate_optimization_plan(&annotations).unwrap();
680
681        let profile = optimizer.generate_lambda_profile(&plan);
682
683        assert!(profile.contains("[profile.lambda]"));
684        assert!(profile.contains("opt-level = \"z\""));
685        assert!(profile.contains("lto = true"));
686    }
687
688    #[test]
689    fn test_build_script_generation() {
690        let optimizer = LambdaOptimizer::new();
691        let annotations = LambdaAnnotations::default();
692        let plan = optimizer.generate_optimization_plan(&annotations).unwrap();
693
694        let script = optimizer.generate_optimized_build_script(&plan, &annotations);
695
696        assert!(script.contains("cargo lambda build"));
697        assert!(script.contains("export RUSTFLAGS"));
698        assert!(script.contains("upx --best"));
699    }
700
701    #[test]
702    fn test_performance_estimate() {
703        let optimizer = LambdaOptimizer::new();
704        let annotations = LambdaAnnotations::default();
705        let plan = optimizer.generate_optimization_plan(&annotations).unwrap();
706
707        let estimate = optimizer.estimate_performance_impact(&plan);
708
709        assert!(estimate.binary_size_reduction_percent > 0.0);
710        assert!(estimate.cold_start_improvement_percent >= 0.0);
711    }
712
713    #[test]
714    fn test_aggressive_mode() {
715        let optimizer = LambdaOptimizer::new().enable_aggressive_mode();
716
717        for config in optimizer.strategies.values() {
718            assert!(config.aggressive_mode);
719        }
720    }
721
722    #[test]
723    fn test_custom_performance_targets() {
724        let targets = PerformanceTargets {
725            max_cold_start_ms: 25,
726            max_binary_size_kb: 1024,
727            max_memory_usage_mb: 64,
728            target_throughput_rps: Some(1000),
729        };
730
731        let optimizer = LambdaOptimizer::new().with_targets(targets);
732        assert_eq!(optimizer.performance_targets.max_cold_start_ms, 25);
733        assert_eq!(optimizer.performance_targets.max_binary_size_kb, 1024);
734        assert_eq!(
735            optimizer.performance_targets.target_throughput_rps,
736            Some(1000)
737        );
738    }
739
740    // ============================================================
741    // DEPYLER-COVERAGE-95: Additional comprehensive tests
742    // ============================================================
743
744    #[test]
745    fn test_optimization_strategy_equality() {
746        assert_eq!(
747            OptimizationStrategy::BinarySize,
748            OptimizationStrategy::BinarySize
749        );
750        assert_ne!(
751            OptimizationStrategy::BinarySize,
752            OptimizationStrategy::ColdStart
753        );
754    }
755
756    #[test]
757    fn test_optimization_strategy_hash() {
758        use std::collections::HashSet;
759        let mut set = HashSet::new();
760        set.insert(OptimizationStrategy::BinarySize);
761        set.insert(OptimizationStrategy::ColdStart);
762        set.insert(OptimizationStrategy::PreWarming);
763        set.insert(OptimizationStrategy::MemoryUsage);
764        set.insert(OptimizationStrategy::CompileTime);
765        assert_eq!(set.len(), 5);
766    }
767
768    #[test]
769    fn test_optimization_config_default() {
770        let config = OptimizationConfig::default();
771        assert!(config.enabled);
772        assert!(!config.aggressive_mode);
773        assert_eq!(config.size_threshold_kb, Some(1024));
774        assert_eq!(config.cold_start_threshold_ms, Some(100));
775    }
776
777    #[test]
778    fn test_performance_targets_default() {
779        let targets = PerformanceTargets::default();
780        assert_eq!(targets.max_cold_start_ms, 50);
781        assert_eq!(targets.max_binary_size_kb, 2048);
782        assert_eq!(targets.max_memory_usage_mb, 128);
783        assert!(targets.target_throughput_rps.is_none());
784    }
785
786    #[test]
787    fn test_lambda_optimizer_default() {
788        let optimizer = LambdaOptimizer::default();
789        assert!(optimizer
790            .strategies
791            .contains_key(&OptimizationStrategy::BinarySize));
792        assert!(optimizer
793            .strategies
794            .contains_key(&OptimizationStrategy::ColdStart));
795        assert!(optimizer
796            .strategies
797            .contains_key(&OptimizationStrategy::PreWarming));
798        assert!(optimizer
799            .strategies
800            .contains_key(&OptimizationStrategy::MemoryUsage));
801    }
802
803    #[test]
804    fn test_lambda_optimizer_strategies_count() {
805        let optimizer = LambdaOptimizer::new();
806        assert_eq!(optimizer.strategies.len(), 4);
807    }
808
809    #[test]
810    fn test_is_strategy_enabled_true() {
811        let optimizer = LambdaOptimizer::new();
812        assert!(optimizer.is_strategy_enabled(&OptimizationStrategy::BinarySize));
813        assert!(optimizer.is_strategy_enabled(&OptimizationStrategy::ColdStart));
814    }
815
816    #[test]
817    fn test_is_strategy_enabled_disabled() {
818        let mut optimizer = LambdaOptimizer::new();
819        optimizer
820            .strategies
821            .get_mut(&OptimizationStrategy::PreWarming)
822            .unwrap()
823            .enabled = false;
824        assert!(!optimizer.is_strategy_enabled(&OptimizationStrategy::PreWarming));
825    }
826
827    #[test]
828    fn test_is_strategy_enabled_missing() {
829        let optimizer = LambdaOptimizer::new();
830        assert!(!optimizer.is_strategy_enabled(&OptimizationStrategy::CompileTime));
831    }
832
833    #[test]
834    fn test_generate_lambda_profile_contains_header() {
835        let optimizer = LambdaOptimizer::new();
836        let annotations = LambdaAnnotations::default();
837        let plan = optimizer.generate_optimization_plan(&annotations).unwrap();
838        let profile = optimizer.generate_lambda_profile(&plan);
839        assert!(profile.contains("[profile.lambda]"));
840    }
841
842    #[test]
843    fn test_generate_lambda_profile_contains_package_override() {
844        let optimizer = LambdaOptimizer::new();
845        let annotations = LambdaAnnotations::default();
846        let plan = optimizer.generate_optimization_plan(&annotations).unwrap();
847        let profile = optimizer.generate_lambda_profile(&plan);
848        assert!(profile.contains("[profile.lambda.package.\"*\"]"));
849    }
850
851    #[test]
852    fn test_build_script_arm64() {
853        let optimizer = LambdaOptimizer::new();
854        let annotations = LambdaAnnotations {
855            architecture: Architecture::Arm64,
856            ..Default::default()
857        };
858        let plan = optimizer.generate_optimization_plan(&annotations).unwrap();
859        let script = optimizer.generate_optimized_build_script(&plan, &annotations);
860        assert!(script.contains("--arm64"));
861        assert!(script.contains("ARM64"));
862    }
863
864    #[test]
865    fn test_build_script_x86_64() {
866        let optimizer = LambdaOptimizer::new();
867        let annotations = LambdaAnnotations {
868            architecture: Architecture::X86_64,
869            ..Default::default()
870        };
871        let plan = optimizer.generate_optimization_plan(&annotations).unwrap();
872        let script = optimizer.generate_optimized_build_script(&plan, &annotations);
873        assert!(script.contains("--x86-64"));
874        assert!(script.contains("x86_64"));
875    }
876
877    #[test]
878    fn test_build_script_contains_strip() {
879        let optimizer = LambdaOptimizer::new();
880        let annotations = LambdaAnnotations::default();
881        let plan = optimizer.generate_optimization_plan(&annotations).unwrap();
882        let script = optimizer.generate_optimized_build_script(&plan, &annotations);
883        assert!(script.contains("strip"));
884    }
885
886    #[test]
887    fn test_build_script_contains_memory_size() {
888        let optimizer = LambdaOptimizer::new();
889        let annotations = LambdaAnnotations {
890            memory_size: 256,
891            ..Default::default()
892        };
893        let plan = optimizer.generate_optimization_plan(&annotations).unwrap();
894        let script = optimizer.generate_optimized_build_script(&plan, &annotations);
895        assert!(script.contains("256 MB memory"));
896    }
897
898    #[test]
899    fn test_performance_monitoring_code() {
900        let optimizer = LambdaOptimizer::new();
901        let annotations = LambdaAnnotations::default();
902        let code = optimizer.generate_performance_monitoring(&annotations);
903        assert!(code.contains("PerformanceMonitor"));
904        assert!(code.contains("cold_start_duration_ms"));
905    }
906
907    #[test]
908    fn test_performance_monitoring_memory_usage() {
909        let optimizer = LambdaOptimizer::new();
910        let annotations = LambdaAnnotations::default();
911        let code = optimizer.generate_performance_monitoring(&annotations);
912        assert!(code.contains("log_memory_usage"));
913        assert!(code.contains("VmRSS"));
914    }
915
916    #[test]
917    fn test_estimate_performance_with_lto() {
918        let mut plan = OptimizationPlan {
919            profile_overrides: HashMap::new(),
920            cargo_flags: vec![],
921            rustc_flags: vec!["-Clto=fat".to_string()],
922            pre_warm_code: String::new(),
923            init_array_code: String::new(),
924            dependency_optimizations: vec![],
925        };
926        plan.profile_overrides
927            .insert("opt-level".to_string(), "z".to_string());
928
929        let optimizer = LambdaOptimizer::new();
930        let estimate = optimizer.estimate_performance_impact(&plan);
931        assert!(estimate.binary_size_reduction_percent >= 40.0); // 25 + 15
932    }
933
934    #[test]
935    fn test_estimate_performance_with_strip() {
936        let plan = OptimizationPlan {
937            profile_overrides: HashMap::new(),
938            cargo_flags: vec![],
939            rustc_flags: vec!["-Cstrip=symbols".to_string()],
940            pre_warm_code: String::new(),
941            init_array_code: String::new(),
942            dependency_optimizations: vec![],
943        };
944
945        let optimizer = LambdaOptimizer::new();
946        let estimate = optimizer.estimate_performance_impact(&plan);
947        assert!(estimate.binary_size_reduction_percent >= 30.0);
948    }
949
950    #[test]
951    fn test_estimate_performance_with_prewarm() {
952        let plan = OptimizationPlan {
953            profile_overrides: HashMap::new(),
954            cargo_flags: vec![],
955            rustc_flags: vec![],
956            pre_warm_code: "// Some prewarm code".to_string(),
957            init_array_code: String::new(),
958            dependency_optimizations: vec![],
959        };
960
961        let optimizer = LambdaOptimizer::new();
962        let estimate = optimizer.estimate_performance_impact(&plan);
963        assert!(estimate.cold_start_improvement_percent >= 40.0);
964    }
965
966    #[test]
967    fn test_estimate_performance_with_init_array() {
968        let plan = OptimizationPlan {
969            profile_overrides: HashMap::new(),
970            cargo_flags: vec![],
971            rustc_flags: vec![],
972            pre_warm_code: String::new(),
973            init_array_code: "// Some init code".to_string(),
974            dependency_optimizations: vec![],
975        };
976
977        let optimizer = LambdaOptimizer::new();
978        let estimate = optimizer.estimate_performance_impact(&plan);
979        assert!(estimate.cold_start_improvement_percent >= 20.0);
980    }
981
982    #[test]
983    fn test_estimate_performance_with_mimalloc() {
984        let plan = OptimizationPlan {
985            profile_overrides: HashMap::new(),
986            cargo_flags: vec![],
987            rustc_flags: vec![],
988            pre_warm_code: String::new(),
989            init_array_code: String::new(),
990            dependency_optimizations: vec![DependencyOptimization {
991                crate_name: "mimalloc".to_string(),
992                features: vec![],
993                disabled_features: vec![],
994                replacement: None,
995            }],
996        };
997
998        let optimizer = LambdaOptimizer::new();
999        let estimate = optimizer.estimate_performance_impact(&plan);
1000        assert!(estimate.memory_improvement_percent >= 15.0);
1001    }
1002
1003    #[test]
1004    fn test_performance_estimate_default() {
1005        let estimate = PerformanceEstimate::default();
1006        assert_eq!(estimate.binary_size_reduction_percent, 0.0);
1007        assert_eq!(estimate.cold_start_improvement_percent, 0.0);
1008        assert_eq!(estimate.memory_improvement_percent, 0.0);
1009        assert_eq!(estimate.compile_time_impact_percent, 0.0);
1010    }
1011
1012    #[test]
1013    fn test_s3_event_prewarm() {
1014        let optimizer = LambdaOptimizer::new();
1015        let annotations = LambdaAnnotations {
1016            cold_start_optimize: true,
1017            event_type: Some(LambdaEventType::S3Event),
1018            ..Default::default()
1019        };
1020
1021        let plan = optimizer.generate_optimization_plan(&annotations).unwrap();
1022        assert!(plan.pre_warm_code.contains("S3") || !plan.pre_warm_code.is_empty());
1023    }
1024
1025    #[test]
1026    fn test_dynamodb_event_prewarm() {
1027        let optimizer = LambdaOptimizer::new();
1028        let annotations = LambdaAnnotations {
1029            cold_start_optimize: true,
1030            event_type: Some(LambdaEventType::DynamodbEvent),
1031            ..Default::default()
1032        };
1033
1034        let plan = optimizer.generate_optimization_plan(&annotations).unwrap();
1035        assert!(!plan.pre_warm_code.is_empty());
1036    }
1037
1038    #[test]
1039    fn test_sns_event_prewarm() {
1040        let optimizer = LambdaOptimizer::new();
1041        let annotations = LambdaAnnotations {
1042            cold_start_optimize: true,
1043            event_type: Some(LambdaEventType::SnsEvent),
1044            ..Default::default()
1045        };
1046
1047        let plan = optimizer.generate_optimization_plan(&annotations).unwrap();
1048        assert!(!plan.pre_warm_code.is_empty());
1049    }
1050
1051    #[test]
1052    fn test_eventbridge_event_prewarm() {
1053        let optimizer = LambdaOptimizer::new();
1054        let annotations = LambdaAnnotations {
1055            cold_start_optimize: true,
1056            event_type: Some(LambdaEventType::EventBridgeEvent(None)),
1057            ..Default::default()
1058        };
1059
1060        let plan = optimizer.generate_optimization_plan(&annotations).unwrap();
1061        assert!(!plan.pre_warm_code.is_empty());
1062    }
1063
1064    #[test]
1065    fn test_api_gateway_v2_event_prewarm() {
1066        let optimizer = LambdaOptimizer::new();
1067        let annotations = LambdaAnnotations {
1068            cold_start_optimize: true,
1069            event_type: Some(LambdaEventType::ApiGatewayV2HttpRequest),
1070            ..Default::default()
1071        };
1072
1073        let plan = optimizer.generate_optimization_plan(&annotations).unwrap();
1074        assert!(!plan.pre_warm_code.is_empty());
1075    }
1076
1077    #[test]
1078    fn test_low_memory_optimizations() {
1079        let optimizer = LambdaOptimizer::new();
1080        let annotations = LambdaAnnotations {
1081            memory_size: 64, // Very low memory
1082            ..Default::default()
1083        };
1084
1085        let plan = optimizer.generate_optimization_plan(&annotations).unwrap();
1086        // Should apply memory optimizations
1087        assert!(plan.pre_warm_code.contains("Memory") || !plan.dependency_optimizations.is_empty());
1088    }
1089
1090    #[test]
1091    fn test_high_memory_optimizations() {
1092        let optimizer = LambdaOptimizer::new();
1093        let annotations = LambdaAnnotations {
1094            memory_size: 1024, // High memory
1095            ..Default::default()
1096        };
1097
1098        let plan = optimizer.generate_optimization_plan(&annotations).unwrap();
1099        // Should still generate valid plan
1100        assert!(!plan.profile_overrides.is_empty());
1101    }
1102
1103    #[test]
1104    fn test_optimization_plan_default_has_rustc_flags() {
1105        let optimizer = LambdaOptimizer::new();
1106        let annotations = LambdaAnnotations::default();
1107
1108        let plan = optimizer.generate_optimization_plan(&annotations).unwrap();
1109        // Should have some rustc flags for binary size optimization
1110        assert!(!plan.rustc_flags.is_empty() || !plan.profile_overrides.is_empty());
1111    }
1112
1113    #[test]
1114    fn test_dependency_optimization_struct() {
1115        let dep_opt = DependencyOptimization {
1116            crate_name: "serde".to_string(),
1117            features: vec!["derive".to_string()],
1118            disabled_features: vec!["std".to_string()],
1119            replacement: Some("miniserde".to_string()),
1120        };
1121
1122        assert_eq!(dep_opt.crate_name, "serde");
1123        assert_eq!(dep_opt.features.len(), 1);
1124        assert_eq!(dep_opt.disabled_features.len(), 1);
1125        assert!(dep_opt.replacement.is_some());
1126    }
1127
1128    #[test]
1129    fn test_optimization_plan_struct() {
1130        let plan = OptimizationPlan {
1131            profile_overrides: HashMap::new(),
1132            cargo_flags: vec!["--release".to_string()],
1133            rustc_flags: vec!["-Copt-level=z".to_string()],
1134            pre_warm_code: "prewarm".to_string(),
1135            init_array_code: "init".to_string(),
1136            dependency_optimizations: vec![],
1137        };
1138
1139        assert_eq!(plan.cargo_flags.len(), 1);
1140        assert_eq!(plan.rustc_flags.len(), 1);
1141        assert!(!plan.pre_warm_code.is_empty());
1142        assert!(!plan.init_array_code.is_empty());
1143    }
1144
1145    #[test]
1146    fn test_with_targets_chaining() {
1147        let targets1 = PerformanceTargets {
1148            max_cold_start_ms: 10,
1149            ..Default::default()
1150        };
1151        let targets2 = PerformanceTargets {
1152            max_cold_start_ms: 20,
1153            ..Default::default()
1154        };
1155
1156        let optimizer = LambdaOptimizer::new()
1157            .with_targets(targets1)
1158            .with_targets(targets2);
1159
1160        // Last one wins
1161        assert_eq!(optimizer.performance_targets.max_cold_start_ms, 20);
1162    }
1163
1164    #[test]
1165    fn test_enable_aggressive_mode_chaining() {
1166        let optimizer = LambdaOptimizer::new()
1167            .enable_aggressive_mode()
1168            .enable_aggressive_mode();
1169
1170        for config in optimizer.strategies.values() {
1171            assert!(config.aggressive_mode);
1172        }
1173    }
1174
1175    #[test]
1176    fn test_cold_start_optimize_disabled() {
1177        let optimizer = LambdaOptimizer::new();
1178        let annotations = LambdaAnnotations {
1179            cold_start_optimize: false,
1180            event_type: Some(LambdaEventType::SqsEvent),
1181            ..Default::default()
1182        };
1183
1184        let plan = optimizer.generate_optimization_plan(&annotations).unwrap();
1185        // Cold start specific optimizations should not be applied
1186        // but binary size optimizations still will be
1187        assert!(!plan.profile_overrides.is_empty());
1188    }
1189
1190    #[test]
1191    fn test_no_event_type_optimization() {
1192        let optimizer = LambdaOptimizer::new();
1193        let annotations = LambdaAnnotations {
1194            cold_start_optimize: true,
1195            event_type: None,
1196            ..Default::default()
1197        };
1198
1199        let plan = optimizer.generate_optimization_plan(&annotations).unwrap();
1200        // Should still generate valid plan without event type
1201        assert!(!plan.profile_overrides.is_empty());
1202    }
1203}