Skip to main content

optirs_bench/ci_cd_automation/
config.rs

1// CI/CD Automation Configuration Management
2//
3// This module provides comprehensive configuration management for CI/CD automation,
4// including platform settings, test execution parameters, baseline management,
5// reporting configuration, artifact storage, and integration settings.
6
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9use std::path::PathBuf;
10use std::time::Duration;
11
12/// CI/CD automation configuration
13#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct CiCdAutomationConfig {
15    /// Enable automated performance testing
16    pub enable_automation: bool,
17    /// CI/CD platform type
18    pub platform: CiCdPlatform,
19    /// Test execution configuration
20    pub test_execution: TestExecutionConfig,
21    /// Baseline management settings
22    pub baseline_management: BaselineManagementConfig,
23    /// Report generation settings
24    pub reporting: ReportingConfig,
25    /// Artifact storage settings
26    pub artifact_storage: ArtifactStorageConfig,
27    /// Integration settings
28    pub integrations: IntegrationConfig,
29    /// Performance gates configuration
30    pub performance_gates: PerformanceGatesConfig,
31}
32
33/// Supported CI/CD platforms
34#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
35pub enum CiCdPlatform {
36    /// GitHub Actions workflow automation
37    GitHubActions,
38    /// GitLab CI/CD pipelines
39    GitLabCI,
40    /// Jenkins automation server
41    Jenkins,
42    /// Azure DevOps Services
43    AzureDevOps,
44    /// CircleCI continuous integration
45    CircleCI,
46    /// Travis CI (legacy support)
47    TravisCI,
48    /// JetBrains TeamCity
49    TeamCity,
50    /// Buildkite CI/CD platform
51    Buildkite,
52    /// Generic/custom CI/CD platform
53    Generic,
54}
55
56/// Test execution configuration
57#[derive(Debug, Clone, Serialize, Deserialize)]
58pub struct TestExecutionConfig {
59    /// Run tests on every commit
60    pub run_on_commit: bool,
61    /// Run tests on pull requests
62    pub run_on_pr: bool,
63    /// Run tests on releases
64    pub run_on_release: bool,
65    /// Run tests on schedule
66    pub run_on_schedule: Option<CronSchedule>,
67    /// Test timeout in seconds
68    pub test_timeout: u64,
69    /// Number of test iterations
70    pub test_iterations: usize,
71    /// Warmup iterations before measurement
72    pub warmup_iterations: usize,
73    /// Parallel test execution
74    pub parallel_execution: bool,
75    /// Test isolation level
76    pub isolation_level: TestIsolationLevel,
77    /// Maximum number of concurrent tests
78    pub max_concurrent_tests: usize,
79    /// Resource limits for test execution
80    pub resource_limits: ResourceLimits,
81    /// Test environment variables
82    pub environment_variables: HashMap<String, String>,
83    /// Custom test command overrides
84    pub custom_commands: HashMap<String, String>,
85}
86
87/// Test isolation levels
88#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
89pub enum TestIsolationLevel {
90    /// Run tests in same process
91    Process,
92    /// Run each test in separate process
93    ProcessIsolated,
94    /// Run tests in Docker containers
95    Container,
96    /// Run tests in virtual machines
97    VirtualMachine,
98}
99
100/// Resource limits for test execution
101#[derive(Debug, Clone, Serialize, Deserialize)]
102pub struct ResourceLimits {
103    /// Maximum memory usage in MB
104    pub max_memory_mb: Option<usize>,
105    /// Maximum CPU usage percentage
106    pub max_cpu_percent: Option<f64>,
107    /// Maximum execution time per test in seconds
108    pub max_execution_time_sec: Option<u64>,
109    /// Maximum disk usage in MB
110    pub max_disk_mb: Option<usize>,
111    /// Maximum network bandwidth in MB/s
112    pub max_network_mbps: Option<f64>,
113}
114
115/// Cron schedule configuration
116#[derive(Debug, Clone, Serialize, Deserialize)]
117pub struct CronSchedule {
118    /// Cron expression (e.g., "0 0 * * *" for daily at midnight)
119    pub expression: String,
120    /// Timezone for schedule execution
121    pub timezone: String,
122    /// Enable/disable scheduled execution
123    pub enabled: bool,
124}
125
126/// Baseline management configuration
127#[derive(Debug, Clone, Serialize, Deserialize)]
128pub struct BaselineManagementConfig {
129    /// Automatically update baseline on main branch
130    pub auto_update_main: bool,
131    /// Update baseline on release tags
132    pub update_on_release: bool,
133    /// Minimum improvement threshold for baseline updates
134    pub min_improvement_threshold: f64,
135    /// Maximum degradation allowed before failing
136    pub max_degradation_threshold: f64,
137    /// Baseline storage configuration
138    pub storage: BaselineStorageConfig,
139    /// Retention policy for baselines
140    pub retention: BaselineRetentionPolicy,
141    /// Baseline validation settings
142    pub validation: BaselineValidationConfig,
143}
144
145/// Baseline storage configuration
146#[derive(Debug, Clone, Serialize, Deserialize)]
147pub struct BaselineStorageConfig {
148    /// Storage provider type
149    pub provider: BaselineStorageProvider,
150    /// Storage location/path
151    pub location: String,
152    /// Encryption settings
153    pub encryption: Option<EncryptionConfig>,
154    /// Compression settings
155    pub compression: Option<CompressionConfig>,
156}
157
158/// Baseline storage providers
159#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
160pub enum BaselineStorageProvider {
161    /// Local filesystem storage
162    Local,
163    /// AWS S3 storage
164    S3,
165    /// Google Cloud Storage
166    GCS,
167    /// Azure Blob Storage
168    AzureBlob,
169    /// Git repository storage
170    Git,
171    /// Database storage
172    Database,
173}
174
175/// Encryption configuration
176#[derive(Debug, Clone, Serialize, Deserialize)]
177pub struct EncryptionConfig {
178    /// Encryption algorithm
179    pub algorithm: EncryptionAlgorithm,
180    /// Key management configuration
181    pub key_management: KeyManagementConfig,
182    /// Enable encryption at rest
183    pub encrypt_at_rest: bool,
184    /// Enable encryption in transit
185    pub encrypt_in_transit: bool,
186}
187
188/// Encryption algorithms
189#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
190pub enum EncryptionAlgorithm {
191    /// AES-256 encryption
192    AES256,
193    /// ChaCha20-Poly1305 encryption
194    ChaCha20Poly1305,
195    /// RSA encryption
196    RSA,
197}
198
199/// Key management configuration
200#[derive(Debug, Clone, Serialize, Deserialize)]
201pub struct KeyManagementConfig {
202    /// Key provider
203    pub provider: KeyProvider,
204    /// Key rotation interval in days
205    pub rotation_interval_days: Option<u32>,
206    /// Key derivation settings
207    pub derivation: Option<KeyDerivationConfig>,
208}
209
210/// Key providers
211#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
212pub enum KeyProvider {
213    /// Environment variables
214    Environment,
215    /// AWS KMS
216    AWSKMS,
217    /// Azure Key Vault
218    AzureKeyVault,
219    /// Google Cloud KMS
220    GoogleCloudKMS,
221    /// HashiCorp Vault
222    Vault,
223}
224
225/// Key derivation configuration
226#[derive(Debug, Clone, Serialize, Deserialize)]
227pub struct KeyDerivationConfig {
228    /// Derivation function
229    pub function: KeyDerivationFunction,
230    /// Salt length in bytes
231    pub salt_length: usize,
232    /// Number of iterations
233    pub iterations: u32,
234}
235
236/// Key derivation functions
237#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
238pub enum KeyDerivationFunction {
239    /// PBKDF2 with SHA-256
240    PBKDF2SHA256,
241    /// Argon2id
242    Argon2id,
243    /// scrypt
244    Scrypt,
245}
246
247/// Compression configuration
248#[derive(Debug, Clone, Serialize, Deserialize)]
249pub struct CompressionConfig {
250    /// Compression algorithm
251    pub algorithm: CompressionAlgorithm,
252    /// Compression level (1-9)
253    pub level: u8,
254    /// Enable compression for baselines
255    pub compress_baselines: bool,
256    /// Enable compression for reports
257    pub compress_reports: bool,
258    /// Minimum file size for compression (bytes)
259    pub min_size_bytes: usize,
260}
261
262/// Compression algorithms
263#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
264pub enum CompressionAlgorithm {
265    /// Gzip compression
266    Gzip,
267    /// Zstandard compression
268    Zstd,
269    /// LZ4 compression
270    LZ4,
271    /// Brotli compression
272    Brotli,
273}
274
275/// Baseline retention policy
276#[derive(Debug, Clone, Serialize, Deserialize)]
277pub struct BaselineRetentionPolicy {
278    /// Keep baselines for N days
279    pub retention_days: u32,
280    /// Maximum number of baselines to keep
281    pub max_baselines: usize,
282    /// Keep all release baselines
283    pub keep_release_baselines: bool,
284    /// Keep milestone baselines
285    pub keep_milestone_baselines: bool,
286    /// Cleanup frequency in hours
287    pub cleanup_frequency_hours: u32,
288}
289
290/// Baseline validation configuration
291#[derive(Debug, Clone, Serialize, Deserialize)]
292pub struct BaselineValidationConfig {
293    /// Enable baseline integrity checks
294    pub enable_integrity_checks: bool,
295    /// Enable statistical validation
296    pub enable_statistical_validation: bool,
297    /// Minimum sample size for validation
298    pub min_sample_size: usize,
299    /// Statistical confidence level
300    pub confidence_level: f64,
301    /// Validation timeout in seconds
302    pub validation_timeout_sec: u64,
303}
304
305/// Report generation configuration
306#[derive(Debug, Clone, Serialize, Deserialize)]
307pub struct ReportingConfig {
308    /// Generate HTML reports
309    pub generate_html: bool,
310    /// Generate JSON reports
311    pub generate_json: bool,
312    /// Generate JUnit XML reports
313    pub generate_junit: bool,
314    /// Generate Markdown reports
315    pub generate_markdown: bool,
316    /// Generate PDF reports
317    pub generate_pdf: bool,
318    /// Include detailed metrics
319    pub include_detailed_metrics: bool,
320    /// Include performance graphs
321    pub include_graphs: bool,
322    /// Include regression analysis
323    pub include_regression_analysis: bool,
324    /// Report templates configuration
325    pub templates: ReportTemplateConfig,
326    /// Report styling configuration
327    pub styling: ReportStylingConfig,
328    /// Report distribution configuration
329    pub distribution: ReportDistributionConfig,
330}
331
332/// Report template configuration
333#[derive(Debug, Clone, Serialize, Deserialize)]
334pub struct ReportTemplateConfig {
335    /// Custom HTML template path
336    pub html_template_path: Option<PathBuf>,
337    /// Custom Markdown template path
338    pub markdown_template_path: Option<PathBuf>,
339    /// Template variables
340    pub template_variables: HashMap<String, String>,
341    /// Enable template caching
342    pub enable_caching: bool,
343}
344
345/// Report styling configuration
346#[derive(Debug, Clone, Serialize, Deserialize)]
347pub struct ReportStylingConfig {
348    /// CSS style sheet path
349    pub css_path: Option<PathBuf>,
350    /// Color theme
351    pub color_theme: ColorTheme,
352    /// Font family
353    pub font_family: String,
354    /// Chart styling
355    pub chart_style: ChartStyleConfig,
356}
357
358/// Color themes for reports
359#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
360pub enum ColorTheme {
361    /// Light theme
362    Light,
363    /// Dark theme
364    Dark,
365    /// High contrast theme
366    HighContrast,
367    /// Custom theme
368    Custom,
369}
370
371/// Chart styling configuration
372#[derive(Debug, Clone, Serialize, Deserialize)]
373pub struct ChartStyleConfig {
374    /// Chart width in pixels
375    pub width: u32,
376    /// Chart height in pixels
377    pub height: u32,
378    /// Enable animations
379    pub enable_animations: bool,
380    /// Color palette
381    pub color_palette: Vec<String>,
382    /// Grid style
383    pub grid_style: GridStyle,
384}
385
386/// Grid styles for charts
387#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
388pub enum GridStyle {
389    /// Solid grid lines
390    Solid,
391    /// Dashed grid lines
392    Dashed,
393    /// Dotted grid lines
394    Dotted,
395    /// No grid lines
396    None,
397}
398
399/// Report distribution configuration
400#[derive(Debug, Clone, Serialize, Deserialize)]
401pub struct ReportDistributionConfig {
402    /// Email distribution settings
403    pub email: Option<EmailDistributionConfig>,
404    /// Slack distribution settings
405    pub slack: Option<SlackDistributionConfig>,
406    /// Webhook distribution settings
407    pub webhooks: Vec<WebhookDistributionConfig>,
408    /// File system distribution
409    pub filesystem: Option<FilesystemDistributionConfig>,
410}
411
412/// Email distribution configuration
413#[derive(Debug, Clone, Serialize, Deserialize)]
414pub struct EmailDistributionConfig {
415    /// Recipients list
416    pub recipients: Vec<String>,
417    /// Subject template
418    pub subject_template: String,
419    /// Email body template
420    pub body_template: String,
421    /// Attach reports as files
422    pub attach_reports: bool,
423    /// Email priority
424    pub priority: EmailPriority,
425}
426
427/// Email priority levels
428#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
429pub enum EmailPriority {
430    /// Low priority
431    Low,
432    /// Normal priority
433    Normal,
434    /// High priority
435    High,
436    /// Urgent priority
437    Urgent,
438}
439
440/// Slack distribution configuration
441#[derive(Debug, Clone, Serialize, Deserialize)]
442pub struct SlackDistributionConfig {
443    /// Slack channels
444    pub channels: Vec<String>,
445    /// Message template
446    pub message_template: String,
447    /// Include report attachments
448    pub include_attachments: bool,
449    /// Mention users on failures
450    pub mention_on_failure: Vec<String>,
451}
452
453/// Webhook distribution configuration
454#[derive(Debug, Clone, Serialize, Deserialize)]
455pub struct WebhookDistributionConfig {
456    /// Webhook URL
457    pub url: String,
458    /// HTTP method
459    pub method: HttpMethod,
460    /// Headers
461    pub headers: HashMap<String, String>,
462    /// Payload template
463    pub payload_template: String,
464    /// Authentication
465    pub auth: Option<WebhookAuth>,
466    /// Timeout in seconds
467    pub timeout_sec: u64,
468    /// Retry configuration
469    pub retry: WebhookRetryConfig,
470}
471
472/// HTTP methods for webhooks
473#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
474pub enum HttpMethod {
475    GET,
476    POST,
477    PUT,
478    PATCH,
479    DELETE,
480}
481
482/// Webhook authentication methods
483#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
484pub enum WebhookAuth {
485    /// Bearer token authentication
486    Bearer { token: String },
487    /// Basic authentication
488    Basic { username: String, password: String },
489    /// API key authentication
490    ApiKey { key: String, header: String },
491    /// Custom header authentication
492    Custom { headers: HashMap<String, String> },
493}
494
495/// Webhook retry configuration
496#[derive(Debug, Clone, Serialize, Deserialize)]
497pub struct WebhookRetryConfig {
498    /// Maximum number of retries
499    pub max_retries: u32,
500    /// Initial delay in seconds
501    pub initial_delay_sec: u64,
502    /// Backoff multiplier
503    pub backoff_multiplier: f64,
504    /// Maximum delay in seconds
505    pub max_delay_sec: u64,
506}
507
508/// Filesystem distribution configuration
509#[derive(Debug, Clone, Serialize, Deserialize)]
510pub struct FilesystemDistributionConfig {
511    /// Output directory
512    pub output_directory: PathBuf,
513    /// File naming pattern
514    pub file_naming_pattern: String,
515    /// Create subdirectories by date
516    pub create_date_subdirs: bool,
517    /// File permissions (Unix-style)
518    pub file_permissions: Option<u32>,
519}
520
521/// Artifact storage configuration
522#[derive(Debug, Clone, Serialize, Deserialize)]
523pub struct ArtifactStorageConfig {
524    /// Enable artifact storage
525    pub enabled: bool,
526    /// Storage provider
527    pub provider: ArtifactStorageProvider,
528    /// Provider-specific configuration
529    pub storage_config: HashMap<String, String>,
530    /// Retention policy
531    pub retention: ArtifactRetentionPolicy,
532    /// Upload configuration
533    pub upload: ArtifactUploadConfig,
534    /// Download configuration
535    pub download: ArtifactDownloadConfig,
536}
537
538/// Artifact storage providers
539#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
540pub enum ArtifactStorageProvider {
541    /// Local filesystem storage
542    Local(PathBuf),
543    /// AWS S3 storage
544    S3 {
545        bucket: String,
546        region: String,
547        prefix: Option<String>,
548    },
549    /// Google Cloud Storage
550    GCS {
551        bucket: String,
552        prefix: Option<String>,
553    },
554    /// Azure Blob Storage
555    AzureBlob {
556        account: String,
557        container: String,
558        prefix: Option<String>,
559    },
560    /// FTP/SFTP storage
561    FTP {
562        host: String,
563        port: u16,
564        path: String,
565        secure: bool,
566    },
567    /// HTTP/HTTPS storage
568    HTTP {
569        base_url: String,
570        auth: Option<WebhookAuth>,
571    },
572}
573
574/// Artifact retention policy
575#[derive(Debug, Clone, Serialize, Deserialize)]
576pub struct ArtifactRetentionPolicy {
577    /// Retention period in days
578    pub retention_days: u32,
579    /// Maximum number of artifacts to keep
580    pub max_artifacts: Option<usize>,
581    /// Keep artifacts for releases
582    pub keep_release_artifacts: bool,
583    /// Keep artifacts for failed builds
584    pub keep_failed_build_artifacts: bool,
585    /// Cleanup schedule
586    pub cleanup_schedule: Option<CronSchedule>,
587}
588
589/// Artifact upload configuration
590#[derive(Debug, Clone, Serialize, Deserialize)]
591pub struct ArtifactUploadConfig {
592    /// Enable compression during upload
593    pub compress: bool,
594    /// Compression level (1-9)
595    pub compression_level: u8,
596    /// Enable encryption during upload
597    pub encrypt: bool,
598    /// Upload timeout in seconds
599    pub timeout_sec: u64,
600    /// Maximum file size in MB
601    pub max_file_size_mb: usize,
602    /// Parallel upload settings
603    pub parallel_uploads: ParallelUploadConfig,
604}
605
606/// Parallel upload configuration
607#[derive(Debug, Clone, Serialize, Deserialize)]
608pub struct ParallelUploadConfig {
609    /// Enable parallel uploads
610    pub enabled: bool,
611    /// Maximum concurrent uploads
612    pub max_concurrent: usize,
613    /// Chunk size for multipart uploads (MB)
614    pub chunk_size_mb: usize,
615}
616
617/// Artifact download configuration
618#[derive(Debug, Clone, Serialize, Deserialize)]
619pub struct ArtifactDownloadConfig {
620    /// Download timeout in seconds
621    pub timeout_sec: u64,
622    /// Enable download caching
623    pub enable_caching: bool,
624    /// Cache directory
625    pub cache_directory: Option<PathBuf>,
626    /// Cache size limit in MB
627    pub cache_size_limit_mb: Option<usize>,
628    /// Verify checksums on download
629    pub verify_checksums: bool,
630}
631
632/// Integration configuration
633#[derive(Debug, Clone, Serialize, Deserialize, Default)]
634pub struct IntegrationConfig {
635    /// GitHub integration settings
636    pub github: Option<GitHubIntegration>,
637    /// Slack integration settings
638    pub slack: Option<SlackIntegration>,
639    /// Email integration settings
640    pub email: Option<EmailIntegration>,
641    /// Webhook integrations
642    pub webhooks: Vec<WebhookIntegration>,
643    /// Custom integrations
644    pub custom: HashMap<String, CustomIntegrationConfig>,
645}
646
647/// GitHub integration configuration
648#[derive(Debug, Clone, Serialize, Deserialize)]
649pub struct GitHubIntegration {
650    /// GitHub token for API access
651    pub token: String,
652    /// Repository owner
653    pub owner: String,
654    /// Repository name
655    pub repository: String,
656    /// Create status checks
657    pub create_status_checks: bool,
658    /// Create comments on PRs
659    pub create_pr_comments: bool,
660    /// Create issues for regressions
661    pub create_regression_issues: bool,
662    /// Label configuration
663    pub labels: GitHubLabelConfig,
664    /// Status check configuration
665    pub status_checks: GitHubStatusCheckConfig,
666}
667
668/// GitHub label configuration
669#[derive(Debug, Clone, Serialize, Deserialize)]
670pub struct GitHubLabelConfig {
671    /// Performance regression label
672    pub performance_regression: String,
673    /// Performance improvement label
674    pub performance_improvement: String,
675    /// Test failure label
676    pub test_failure: String,
677    /// Automated label
678    pub automated: String,
679}
680
681/// GitHub status check configuration
682#[derive(Debug, Clone, Serialize, Deserialize)]
683pub struct GitHubStatusCheckConfig {
684    /// Status check context name
685    pub context: String,
686    /// Success description
687    pub success_description: String,
688    /// Failure description
689    pub failure_description: String,
690    /// Pending description
691    pub pending_description: String,
692}
693
694/// Slack integration configuration
695#[derive(Debug, Clone, Serialize, Deserialize)]
696pub struct SlackIntegration {
697    /// Slack webhook URL
698    pub webhook_url: String,
699    /// Default channel
700    pub default_channel: String,
701    /// Bot username
702    pub username: Option<String>,
703    /// Bot icon emoji
704    pub icon_emoji: Option<String>,
705    /// Notification settings
706    pub notifications: SlackNotificationConfig,
707}
708
709/// Slack notification configuration
710#[derive(Debug, Clone, Serialize, Deserialize)]
711pub struct SlackNotificationConfig {
712    /// Notify on test completion
713    pub notify_on_completion: bool,
714    /// Notify on regression detection
715    pub notify_on_regression: bool,
716    /// Notify on test failures
717    pub notify_on_failure: bool,
718    /// Notify on performance improvements
719    pub notify_on_improvement: bool,
720    /// Mention users for critical issues
721    pub mention_users: Vec<String>,
722}
723
724/// Email integration configuration
725#[derive(Debug, Clone, Serialize, Deserialize)]
726pub struct EmailIntegration {
727    /// SMTP server configuration
728    pub smtp: SmtpConfig,
729    /// Default sender email
730    pub from_email: String,
731    /// Default recipient emails
732    pub default_recipients: Vec<String>,
733    /// Email templates
734    pub templates: EmailTemplateConfig,
735}
736
737/// SMTP configuration
738#[derive(Debug, Clone, Serialize, Deserialize)]
739pub struct SmtpConfig {
740    /// SMTP server host
741    pub host: String,
742    /// SMTP server port
743    pub port: u16,
744    /// Use TLS encryption
745    pub use_tls: bool,
746    /// Username for authentication
747    pub username: Option<String>,
748    /// Password for authentication
749    pub password: Option<String>,
750    /// Connection timeout in seconds
751    pub timeout_sec: u64,
752}
753
754/// Email template configuration
755#[derive(Debug, Clone, Serialize, Deserialize)]
756pub struct EmailTemplateConfig {
757    /// Subject template for success
758    pub success_subject: String,
759    /// Subject template for failure
760    pub failure_subject: String,
761    /// Body template for success
762    pub success_body: String,
763    /// Body template for failure
764    pub failure_body: String,
765    /// Include HTML formatting
766    pub use_html: bool,
767}
768
769/// Webhook integration configuration
770#[derive(Debug, Clone, Serialize, Deserialize)]
771pub struct WebhookIntegration {
772    /// Webhook name/identifier
773    pub name: String,
774    /// Webhook URL
775    pub url: String,
776    /// HTTP method to use
777    pub method: HttpMethod,
778    /// HTTP headers
779    pub headers: HashMap<String, String>,
780    /// Authentication method
781    pub auth: Option<WebhookAuth>,
782    /// Trigger conditions
783    pub triggers: WebhookTriggerConfig,
784    /// Payload configuration
785    pub payload: WebhookPayloadConfig,
786}
787
788/// Webhook trigger configuration
789#[derive(Debug, Clone, Serialize, Deserialize)]
790pub struct WebhookTriggerConfig {
791    /// Trigger on test completion
792    pub on_completion: bool,
793    /// Trigger on regression detection
794    pub on_regression: bool,
795    /// Trigger on test failure
796    pub on_failure: bool,
797    /// Trigger on performance improvement
798    pub on_improvement: bool,
799    /// Custom trigger conditions
800    pub custom_conditions: Vec<String>,
801}
802
803/// Webhook payload configuration
804#[derive(Debug, Clone, Serialize, Deserialize)]
805pub struct WebhookPayloadConfig {
806    /// Payload format
807    pub format: PayloadFormat,
808    /// Include test results
809    pub include_results: bool,
810    /// Include metrics
811    pub include_metrics: bool,
812    /// Include environment info
813    pub include_environment: bool,
814    /// Custom payload template
815    pub custom_template: Option<String>,
816}
817
818/// Payload formats for webhooks
819#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
820pub enum PayloadFormat {
821    /// JSON format
822    JSON,
823    /// XML format
824    XML,
825    /// Form data format
826    FormData,
827    /// Custom format
828    Custom,
829}
830
831/// Custom integration configuration
832#[derive(Debug, Clone, Serialize, Deserialize)]
833pub struct CustomIntegrationConfig {
834    /// Integration type
835    pub integration_type: String,
836    /// Configuration parameters
837    pub parameters: HashMap<String, String>,
838    /// Enable/disable the integration
839    pub enabled: bool,
840}
841
842/// Performance gates configuration
843#[derive(Debug, Clone, Serialize, Deserialize)]
844pub struct PerformanceGatesConfig {
845    /// Enable performance gates
846    pub enabled: bool,
847    /// Metric gates configuration
848    pub metric_gates: HashMap<MetricType, MetricGate>,
849    /// Gate evaluation strategy
850    pub evaluation_strategy: GateEvaluationStrategy,
851    /// Failure handling configuration
852    pub failure_handling: GateFailureHandling,
853}
854
855/// Metric types for performance gates
856#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
857pub enum MetricType {
858    /// Execution time metric
859    ExecutionTime,
860    /// Memory usage metric
861    MemoryUsage,
862    /// CPU usage metric
863    CpuUsage,
864    /// Throughput metric
865    Throughput,
866    /// Latency metric
867    Latency,
868    /// Error rate metric
869    ErrorRate,
870    /// Custom metric
871    Custom(String),
872}
873
874/// Metric gate configuration
875#[derive(Debug, Clone, Serialize, Deserialize)]
876pub struct MetricGate {
877    /// Gate type
878    pub gate_type: GateType,
879    /// Threshold value
880    pub threshold: f64,
881    /// Comparison operator
882    pub operator: ComparisonOperator,
883    /// Gate severity
884    pub severity: GateSeverity,
885    /// Enable/disable the gate
886    pub enabled: bool,
887}
888
889/// Gate types
890#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
891pub enum GateType {
892    /// Absolute threshold gate
893    Absolute,
894    /// Relative threshold gate (percentage change)
895    Relative,
896    /// Statistical threshold gate
897    Statistical,
898    /// Trend-based gate
899    Trend,
900}
901
902/// Comparison operators for gates
903#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
904pub enum ComparisonOperator {
905    /// Less than
906    LessThan,
907    /// Less than or equal
908    LessThanOrEqual,
909    /// Greater than
910    GreaterThan,
911    /// Greater than or equal
912    GreaterThanOrEqual,
913    /// Equal
914    Equal,
915    /// Not equal
916    NotEqual,
917}
918
919/// Gate severity levels
920#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
921pub enum GateSeverity {
922    /// Informational (does not fail build)
923    Info,
924    /// Warning (logs warning but does not fail build)
925    Warning,
926    /// Error (fails build)
927    Error,
928    /// Critical (fails build and sends alerts)
929    Critical,
930}
931
932/// Gate evaluation strategies
933#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
934pub enum GateEvaluationStrategy {
935    /// Fail fast - stop on first gate failure
936    FailFast,
937    /// Evaluate all gates before failing
938    EvaluateAll,
939    /// Weighted evaluation based on severity
940    Weighted,
941}
942
943/// Gate failure handling configuration
944#[derive(Debug, Clone, Serialize, Deserialize)]
945pub struct GateFailureHandling {
946    /// Action to take on gate failure
947    pub failure_action: GateFailureAction,
948    /// Allow manual override of gate failures
949    pub allow_manual_override: bool,
950    /// Override timeout in hours
951    pub override_timeout_hours: Option<u32>,
952    /// Notification settings for failures
953    pub notifications: GateFailureNotificationConfig,
954}
955
956/// Gate failure actions
957#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
958pub enum GateFailureAction {
959    /// Fail the build
960    FailBuild,
961    /// Mark build as unstable
962    MarkUnstable,
963    /// Log warning and continue
964    LogWarning,
965    /// Send notification only
966    NotifyOnly,
967}
968
969/// Gate failure notification configuration
970#[derive(Debug, Clone, Serialize, Deserialize, Default)]
971pub struct GateFailureNotificationConfig {
972    /// Send email notifications
973    pub send_email: bool,
974    /// Send Slack notifications
975    pub send_slack: bool,
976    /// Send webhook notifications
977    pub send_webhooks: bool,
978    /// Create GitHub issues
979    pub create_github_issues: bool,
980    /// Escalation settings
981    pub escalation: Option<NotificationEscalationConfig>,
982}
983
984/// Notification escalation configuration
985#[derive(Debug, Clone, Serialize, Deserialize)]
986pub struct NotificationEscalationConfig {
987    /// Escalation delay in minutes
988    pub delay_minutes: u32,
989    /// Maximum escalation levels
990    pub max_levels: u32,
991    /// Escalation recipients by level
992    pub recipients_by_level: HashMap<u32, Vec<String>>,
993}
994
995// Default implementations
996
997impl Default for CiCdAutomationConfig {
998    fn default() -> Self {
999        Self {
1000            enable_automation: true,
1001            platform: CiCdPlatform::Generic,
1002            test_execution: TestExecutionConfig::default(),
1003            baseline_management: BaselineManagementConfig::default(),
1004            reporting: ReportingConfig::default(),
1005            artifact_storage: ArtifactStorageConfig::default(),
1006            integrations: IntegrationConfig::default(),
1007            performance_gates: PerformanceGatesConfig::default(),
1008        }
1009    }
1010}
1011
1012impl Default for TestExecutionConfig {
1013    fn default() -> Self {
1014        Self {
1015            run_on_commit: true,
1016            run_on_pr: true,
1017            run_on_release: true,
1018            run_on_schedule: None,
1019            test_timeout: 3600, // 1 hour
1020            test_iterations: 5,
1021            warmup_iterations: 2,
1022            parallel_execution: true,
1023            isolation_level: TestIsolationLevel::Process,
1024            max_concurrent_tests: num_cpus::get(),
1025            resource_limits: ResourceLimits::default(),
1026            environment_variables: HashMap::new(),
1027            custom_commands: HashMap::new(),
1028        }
1029    }
1030}
1031
1032impl Default for ResourceLimits {
1033    fn default() -> Self {
1034        Self {
1035            max_memory_mb: Some(4096),          // 4GB
1036            max_cpu_percent: Some(80.0),        // 80%
1037            max_execution_time_sec: Some(1800), // 30 minutes
1038            max_disk_mb: Some(10240),           // 10GB
1039            max_network_mbps: Some(100.0),      // 100 MB/s
1040        }
1041    }
1042}
1043
1044impl Default for BaselineManagementConfig {
1045    fn default() -> Self {
1046        Self {
1047            auto_update_main: true,
1048            update_on_release: true,
1049            min_improvement_threshold: 0.05, // 5%
1050            max_degradation_threshold: 0.10, // 10%
1051            storage: BaselineStorageConfig::default(),
1052            retention: BaselineRetentionPolicy::default(),
1053            validation: BaselineValidationConfig::default(),
1054        }
1055    }
1056}
1057
1058impl Default for BaselineStorageConfig {
1059    fn default() -> Self {
1060        Self {
1061            provider: BaselineStorageProvider::Local,
1062            location: "./baselines".to_string(),
1063            encryption: None,
1064            compression: None,
1065        }
1066    }
1067}
1068
1069impl Default for BaselineRetentionPolicy {
1070    fn default() -> Self {
1071        Self {
1072            retention_days: 90,
1073            max_baselines: 100,
1074            keep_release_baselines: true,
1075            keep_milestone_baselines: true,
1076            cleanup_frequency_hours: 24,
1077        }
1078    }
1079}
1080
1081impl Default for BaselineValidationConfig {
1082    fn default() -> Self {
1083        Self {
1084            enable_integrity_checks: true,
1085            enable_statistical_validation: true,
1086            min_sample_size: 10,
1087            confidence_level: 0.95,
1088            validation_timeout_sec: 300,
1089        }
1090    }
1091}
1092
1093impl Default for ReportingConfig {
1094    fn default() -> Self {
1095        Self {
1096            generate_html: true,
1097            generate_json: true,
1098            generate_junit: false,
1099            generate_markdown: false,
1100            generate_pdf: false,
1101            include_detailed_metrics: true,
1102            include_graphs: true,
1103            include_regression_analysis: true,
1104            templates: ReportTemplateConfig::default(),
1105            styling: ReportStylingConfig::default(),
1106            distribution: ReportDistributionConfig::default(),
1107        }
1108    }
1109}
1110
1111impl Default for ReportTemplateConfig {
1112    fn default() -> Self {
1113        Self {
1114            html_template_path: None,
1115            markdown_template_path: None,
1116            template_variables: HashMap::new(),
1117            enable_caching: true,
1118        }
1119    }
1120}
1121
1122impl Default for ReportStylingConfig {
1123    fn default() -> Self {
1124        Self {
1125            css_path: None,
1126            color_theme: ColorTheme::Light,
1127            font_family: "Arial, sans-serif".to_string(),
1128            chart_style: ChartStyleConfig::default(),
1129        }
1130    }
1131}
1132
1133impl Default for ChartStyleConfig {
1134    fn default() -> Self {
1135        Self {
1136            width: 800,
1137            height: 600,
1138            enable_animations: true,
1139            color_palette: vec![
1140                "#1f77b4".to_string(),
1141                "#ff7f0e".to_string(),
1142                "#2ca02c".to_string(),
1143                "#d62728".to_string(),
1144                "#9467bd".to_string(),
1145            ],
1146            grid_style: GridStyle::Solid,
1147        }
1148    }
1149}
1150
1151impl Default for ReportDistributionConfig {
1152    fn default() -> Self {
1153        Self {
1154            email: None,
1155            slack: None,
1156            webhooks: Vec::new(),
1157            filesystem: Some(FilesystemDistributionConfig::default()),
1158        }
1159    }
1160}
1161
1162impl Default for FilesystemDistributionConfig {
1163    fn default() -> Self {
1164        Self {
1165            output_directory: PathBuf::from("./reports"),
1166            file_naming_pattern: "{timestamp}_{test_name}_{format}".to_string(),
1167            create_date_subdirs: true,
1168            file_permissions: Some(0o644),
1169        }
1170    }
1171}
1172
1173impl Default for ArtifactStorageConfig {
1174    fn default() -> Self {
1175        Self {
1176            enabled: true,
1177            provider: ArtifactStorageProvider::Local(PathBuf::from("./artifacts")),
1178            storage_config: HashMap::new(),
1179            retention: ArtifactRetentionPolicy::default(),
1180            upload: ArtifactUploadConfig::default(),
1181            download: ArtifactDownloadConfig::default(),
1182        }
1183    }
1184}
1185
1186impl Default for ArtifactRetentionPolicy {
1187    fn default() -> Self {
1188        Self {
1189            retention_days: 30,
1190            max_artifacts: Some(1000),
1191            keep_release_artifacts: true,
1192            keep_failed_build_artifacts: false,
1193            cleanup_schedule: None,
1194        }
1195    }
1196}
1197
1198impl Default for ArtifactUploadConfig {
1199    fn default() -> Self {
1200        Self {
1201            compress: true,
1202            compression_level: 6,
1203            encrypt: false,
1204            timeout_sec: 300,
1205            max_file_size_mb: 1024, // 1GB
1206            parallel_uploads: ParallelUploadConfig::default(),
1207        }
1208    }
1209}
1210
1211impl Default for ParallelUploadConfig {
1212    fn default() -> Self {
1213        Self {
1214            enabled: true,
1215            max_concurrent: 4,
1216            chunk_size_mb: 100, // 100MB chunks
1217        }
1218    }
1219}
1220
1221impl Default for ArtifactDownloadConfig {
1222    fn default() -> Self {
1223        Self {
1224            timeout_sec: 300,
1225            enable_caching: true,
1226            cache_directory: Some(PathBuf::from("./cache")),
1227            cache_size_limit_mb: Some(5120), // 5GB
1228            verify_checksums: true,
1229        }
1230    }
1231}
1232
1233impl Default for PerformanceGatesConfig {
1234    fn default() -> Self {
1235        let mut metric_gates = HashMap::new();
1236
1237        // Default execution time gate: fail if > 20% slower
1238        metric_gates.insert(
1239            MetricType::ExecutionTime,
1240            MetricGate {
1241                gate_type: GateType::Relative,
1242                threshold: 0.20, // 20% increase
1243                operator: ComparisonOperator::LessThanOrEqual,
1244                severity: GateSeverity::Error,
1245                enabled: true,
1246            },
1247        );
1248
1249        // Default memory usage gate: fail if > 30% more memory
1250        metric_gates.insert(
1251            MetricType::MemoryUsage,
1252            MetricGate {
1253                gate_type: GateType::Relative,
1254                threshold: 0.30, // 30% increase
1255                operator: ComparisonOperator::LessThanOrEqual,
1256                severity: GateSeverity::Warning,
1257                enabled: true,
1258            },
1259        );
1260
1261        Self {
1262            enabled: true,
1263            metric_gates,
1264            evaluation_strategy: GateEvaluationStrategy::EvaluateAll,
1265            failure_handling: GateFailureHandling::default(),
1266        }
1267    }
1268}
1269
1270impl Default for GateFailureHandling {
1271    fn default() -> Self {
1272        Self {
1273            failure_action: GateFailureAction::FailBuild,
1274            allow_manual_override: true,
1275            override_timeout_hours: Some(24),
1276            notifications: GateFailureNotificationConfig::default(),
1277        }
1278    }
1279}
1280
1281// Configuration validation and utilities
1282
1283impl CiCdAutomationConfig {
1284    /// Validate the configuration
1285    pub fn validate(&self) -> Result<(), String> {
1286        // Validate test execution config
1287        self.test_execution.validate()?;
1288
1289        // Validate baseline management config
1290        self.baseline_management.validate()?;
1291
1292        // Validate reporting config
1293        self.reporting.validate()?;
1294
1295        // Validate artifact storage config
1296        self.artifact_storage.validate()?;
1297
1298        // Validate performance gates config
1299        self.performance_gates.validate()?;
1300
1301        Ok(())
1302    }
1303
1304    /// Get platform-specific configuration
1305    pub fn get_platform_config(&self) -> PlatformSpecificConfig {
1306        match self.platform {
1307            CiCdPlatform::GitHubActions => PlatformSpecificConfig::github_actions(),
1308            CiCdPlatform::GitLabCI => PlatformSpecificConfig::gitlab_ci(),
1309            CiCdPlatform::Jenkins => PlatformSpecificConfig::jenkins(),
1310            CiCdPlatform::AzureDevOps => PlatformSpecificConfig::azure_devops(),
1311            CiCdPlatform::CircleCI => PlatformSpecificConfig::circle_ci(),
1312            CiCdPlatform::TravisCI => PlatformSpecificConfig::travis_ci(),
1313            CiCdPlatform::TeamCity => PlatformSpecificConfig::team_city(),
1314            CiCdPlatform::Buildkite => PlatformSpecificConfig::buildkite(),
1315            CiCdPlatform::Generic => PlatformSpecificConfig::generic(),
1316        }
1317    }
1318}
1319
1320impl TestExecutionConfig {
1321    fn validate(&self) -> Result<(), String> {
1322        if self.test_timeout == 0 {
1323            return Err("Test timeout cannot be zero".to_string());
1324        }
1325
1326        if self.test_iterations == 0 {
1327            return Err("Test iterations cannot be zero".to_string());
1328        }
1329
1330        if self.max_concurrent_tests == 0 {
1331            return Err("Max concurrent tests cannot be zero".to_string());
1332        }
1333
1334        Ok(())
1335    }
1336}
1337
1338impl BaselineManagementConfig {
1339    fn validate(&self) -> Result<(), String> {
1340        if self.min_improvement_threshold < 0.0 || self.min_improvement_threshold > 1.0 {
1341            return Err("Min improvement threshold must be between 0.0 and 1.0".to_string());
1342        }
1343
1344        if self.max_degradation_threshold < 0.0 || self.max_degradation_threshold > 1.0 {
1345            return Err("Max degradation threshold must be between 0.0 and 1.0".to_string());
1346        }
1347
1348        Ok(())
1349    }
1350}
1351
1352impl ReportingConfig {
1353    fn validate(&self) -> Result<(), String> {
1354        if !self.generate_html
1355            && !self.generate_json
1356            && !self.generate_junit
1357            && !self.generate_markdown
1358            && !self.generate_pdf
1359        {
1360            return Err("At least one report format must be enabled".to_string());
1361        }
1362
1363        Ok(())
1364    }
1365}
1366
1367impl ArtifactStorageConfig {
1368    fn validate(&self) -> Result<(), String> {
1369        if self.enabled {
1370            match &self.provider {
1371                ArtifactStorageProvider::Local(path) => {
1372                    if path.as_os_str().is_empty() {
1373                        return Err("Local storage path cannot be empty".to_string());
1374                    }
1375                }
1376                ArtifactStorageProvider::S3 { bucket, .. } => {
1377                    if bucket.is_empty() {
1378                        return Err("S3 bucket name cannot be empty".to_string());
1379                    }
1380                }
1381                ArtifactStorageProvider::GCS { bucket, .. } => {
1382                    if bucket.is_empty() {
1383                        return Err("GCS bucket name cannot be empty".to_string());
1384                    }
1385                }
1386                ArtifactStorageProvider::AzureBlob {
1387                    account, container, ..
1388                } => {
1389                    if account.is_empty() || container.is_empty() {
1390                        return Err("Azure Blob account and container cannot be empty".to_string());
1391                    }
1392                }
1393                ArtifactStorageProvider::FTP { host, .. } => {
1394                    if host.is_empty() {
1395                        return Err("FTP host cannot be empty".to_string());
1396                    }
1397                }
1398                ArtifactStorageProvider::HTTP { base_url, .. } => {
1399                    if base_url.is_empty() {
1400                        return Err("HTTP base URL cannot be empty".to_string());
1401                    }
1402                }
1403            }
1404        }
1405
1406        Ok(())
1407    }
1408}
1409
1410impl PerformanceGatesConfig {
1411    fn validate(&self) -> Result<(), String> {
1412        if self.enabled && self.metric_gates.is_empty() {
1413            return Err("Performance gates are enabled but no gates are configured".to_string());
1414        }
1415
1416        for (metric_type, gate) in &self.metric_gates {
1417            gate.validate(metric_type)?;
1418        }
1419
1420        Ok(())
1421    }
1422}
1423
1424impl MetricGate {
1425    fn validate(&self, _metric_type: &MetricType) -> Result<(), String> {
1426        if self.threshold < 0.0 {
1427            return Err("Gate threshold cannot be negative".to_string());
1428        }
1429
1430        if self.gate_type == GateType::Relative && self.threshold > 10.0 {
1431            // 1000% change seems unreasonable
1432            return Err("Relative gate threshold seems unreasonably high".to_string());
1433        }
1434
1435        Ok(())
1436    }
1437}
1438
1439/// Platform-specific configuration settings
1440#[derive(Debug, Clone)]
1441pub struct PlatformSpecificConfig {
1442    /// Environment variable names for common values
1443    pub env_vars: HashMap<String, String>,
1444    /// Default artifact paths
1445    pub artifact_paths: Vec<String>,
1446    /// Platform-specific commands
1447    pub commands: HashMap<String, String>,
1448    /// Platform limitations
1449    pub limitations: PlatformLimitations,
1450}
1451
1452/// Platform limitations and constraints
1453#[derive(Debug, Clone)]
1454pub struct PlatformLimitations {
1455    /// Maximum job runtime in minutes
1456    pub max_job_runtime_minutes: Option<u32>,
1457    /// Maximum artifact size in MB
1458    pub max_artifact_size_mb: Option<usize>,
1459    /// Maximum number of parallel jobs
1460    pub max_parallel_jobs: Option<usize>,
1461    /// Supported operating systems
1462    pub supported_os: Vec<String>,
1463}
1464
1465impl PlatformSpecificConfig {
1466    fn github_actions() -> Self {
1467        let mut env_vars = HashMap::new();
1468        env_vars.insert("CI".to_string(), "GITHUB_ACTIONS".to_string());
1469        env_vars.insert("BUILD_ID".to_string(), "GITHUB_RUN_ID".to_string());
1470        env_vars.insert("BRANCH".to_string(), "GITHUB_REF_NAME".to_string());
1471        env_vars.insert("COMMIT".to_string(), "GITHUB_SHA".to_string());
1472
1473        let mut commands = HashMap::new();
1474        commands.insert(
1475            "cache_key".to_string(),
1476            "echo \"::set-output name=cache-key::${{ hashFiles('**/Cargo.lock') }}\"".to_string(),
1477        );
1478
1479        Self {
1480            env_vars,
1481            artifact_paths: vec!["./target/criterion".to_string()],
1482            commands,
1483            limitations: PlatformLimitations {
1484                max_job_runtime_minutes: Some(360), // 6 hours
1485                max_artifact_size_mb: Some(2048),   // 2GB
1486                max_parallel_jobs: Some(20),
1487                supported_os: vec![
1488                    "ubuntu-latest".to_string(),
1489                    "windows-latest".to_string(),
1490                    "macos-latest".to_string(),
1491                ],
1492            },
1493        }
1494    }
1495
1496    fn gitlab_ci() -> Self {
1497        let mut env_vars = HashMap::new();
1498        env_vars.insert("CI".to_string(), "GITLAB_CI".to_string());
1499        env_vars.insert("BUILD_ID".to_string(), "CI_JOB_ID".to_string());
1500        env_vars.insert("BRANCH".to_string(), "CI_COMMIT_REF_NAME".to_string());
1501        env_vars.insert("COMMIT".to_string(), "CI_COMMIT_SHA".to_string());
1502
1503        Self {
1504            env_vars,
1505            artifact_paths: vec!["./target/criterion".to_string()],
1506            commands: HashMap::new(),
1507            limitations: PlatformLimitations {
1508                max_job_runtime_minutes: None,    // Configurable
1509                max_artifact_size_mb: Some(1024), // 1GB default
1510                max_parallel_jobs: None,          // Depends on plan
1511                supported_os: vec![
1512                    "linux".to_string(),
1513                    "windows".to_string(),
1514                    "macos".to_string(),
1515                ],
1516            },
1517        }
1518    }
1519
1520    fn jenkins() -> Self {
1521        let mut env_vars = HashMap::new();
1522        env_vars.insert("CI".to_string(), "JENKINS".to_string());
1523        env_vars.insert("BUILD_ID".to_string(), "BUILD_NUMBER".to_string());
1524        env_vars.insert("BRANCH".to_string(), "BRANCH_NAME".to_string());
1525        env_vars.insert("COMMIT".to_string(), "GIT_COMMIT".to_string());
1526
1527        Self {
1528            env_vars,
1529            artifact_paths: vec!["./target/criterion".to_string()],
1530            commands: HashMap::new(),
1531            limitations: PlatformLimitations {
1532                max_job_runtime_minutes: None, // Configurable
1533                max_artifact_size_mb: None,    // Configurable
1534                max_parallel_jobs: None,       // Configurable
1535                supported_os: vec![
1536                    "linux".to_string(),
1537                    "windows".to_string(),
1538                    "macos".to_string(),
1539                ],
1540            },
1541        }
1542    }
1543
1544    fn azure_devops() -> Self {
1545        let mut env_vars = HashMap::new();
1546        env_vars.insert("CI".to_string(), "AZURE_DEVOPS".to_string());
1547        env_vars.insert("BUILD_ID".to_string(), "BUILD_BUILDID".to_string());
1548        env_vars.insert("BRANCH".to_string(), "BUILD_SOURCEBRANCHNAME".to_string());
1549        env_vars.insert("COMMIT".to_string(), "BUILD_SOURCEVERSION".to_string());
1550
1551        Self {
1552            env_vars,
1553            artifact_paths: vec!["./target/criterion".to_string()],
1554            commands: HashMap::new(),
1555            limitations: PlatformLimitations {
1556                max_job_runtime_minutes: Some(360), // 6 hours
1557                max_artifact_size_mb: Some(100),    // 100MB
1558                max_parallel_jobs: Some(10),
1559                supported_os: vec![
1560                    "ubuntu-latest".to_string(),
1561                    "windows-latest".to_string(),
1562                    "macos-latest".to_string(),
1563                ],
1564            },
1565        }
1566    }
1567
1568    fn circle_ci() -> Self {
1569        let mut env_vars = HashMap::new();
1570        env_vars.insert("CI".to_string(), "CIRCLECI".to_string());
1571        env_vars.insert("BUILD_ID".to_string(), "CIRCLE_BUILD_NUM".to_string());
1572        env_vars.insert("BRANCH".to_string(), "CIRCLE_BRANCH".to_string());
1573        env_vars.insert("COMMIT".to_string(), "CIRCLE_SHA1".to_string());
1574
1575        Self {
1576            env_vars,
1577            artifact_paths: vec!["./target/criterion".to_string()],
1578            commands: HashMap::new(),
1579            limitations: PlatformLimitations {
1580                max_job_runtime_minutes: Some(300), // 5 hours
1581                max_artifact_size_mb: Some(3072),   // 3GB
1582                max_parallel_jobs: Some(16),
1583                supported_os: vec![
1584                    "linux".to_string(),
1585                    "macos".to_string(),
1586                    "windows".to_string(),
1587                ],
1588            },
1589        }
1590    }
1591
1592    fn travis_ci() -> Self {
1593        let mut env_vars = HashMap::new();
1594        env_vars.insert("CI".to_string(), "TRAVIS".to_string());
1595        env_vars.insert("BUILD_ID".to_string(), "TRAVIS_BUILD_ID".to_string());
1596        env_vars.insert("BRANCH".to_string(), "TRAVIS_BRANCH".to_string());
1597        env_vars.insert("COMMIT".to_string(), "TRAVIS_COMMIT".to_string());
1598
1599        Self {
1600            env_vars,
1601            artifact_paths: vec!["./target/criterion".to_string()],
1602            commands: HashMap::new(),
1603            limitations: PlatformLimitations {
1604                max_job_runtime_minutes: Some(50), // 50 minutes
1605                max_artifact_size_mb: Some(100),   // 100MB
1606                max_parallel_jobs: Some(5),
1607                supported_os: vec![
1608                    "linux".to_string(),
1609                    "osx".to_string(),
1610                    "windows".to_string(),
1611                ],
1612            },
1613        }
1614    }
1615
1616    fn team_city() -> Self {
1617        let mut env_vars = HashMap::new();
1618        env_vars.insert("CI".to_string(), "TEAMCITY".to_string());
1619        env_vars.insert("BUILD_ID".to_string(), "BUILD_NUMBER".to_string());
1620        env_vars.insert("BRANCH".to_string(), "teamcity.build.branch".to_string());
1621        env_vars.insert("COMMIT".to_string(), "BUILD_VCS_NUMBER".to_string());
1622
1623        Self {
1624            env_vars,
1625            artifact_paths: vec!["./target/criterion".to_string()],
1626            commands: HashMap::new(),
1627            limitations: PlatformLimitations {
1628                max_job_runtime_minutes: None, // Configurable
1629                max_artifact_size_mb: None,    // Configurable
1630                max_parallel_jobs: None,       // Configurable
1631                supported_os: vec![
1632                    "linux".to_string(),
1633                    "windows".to_string(),
1634                    "macos".to_string(),
1635                ],
1636            },
1637        }
1638    }
1639
1640    fn buildkite() -> Self {
1641        let mut env_vars = HashMap::new();
1642        env_vars.insert("CI".to_string(), "BUILDKITE".to_string());
1643        env_vars.insert("BUILD_ID".to_string(), "BUILDKITE_BUILD_ID".to_string());
1644        env_vars.insert("BRANCH".to_string(), "BUILDKITE_BRANCH".to_string());
1645        env_vars.insert("COMMIT".to_string(), "BUILDKITE_COMMIT".to_string());
1646
1647        Self {
1648            env_vars,
1649            artifact_paths: vec!["./target/criterion".to_string()],
1650            commands: HashMap::new(),
1651            limitations: PlatformLimitations {
1652                max_job_runtime_minutes: None, // Configurable
1653                max_artifact_size_mb: None,    // Configurable
1654                max_parallel_jobs: None,       // Configurable
1655                supported_os: vec![
1656                    "linux".to_string(),
1657                    "windows".to_string(),
1658                    "macos".to_string(),
1659                ],
1660            },
1661        }
1662    }
1663
1664    fn generic() -> Self {
1665        let mut env_vars = HashMap::new();
1666        env_vars.insert("CI".to_string(), "true".to_string());
1667        env_vars.insert("BUILD_ID".to_string(), "BUILD_ID".to_string());
1668        env_vars.insert("BRANCH".to_string(), "BRANCH".to_string());
1669        env_vars.insert("COMMIT".to_string(), "COMMIT_SHA".to_string());
1670
1671        Self {
1672            env_vars,
1673            artifact_paths: vec!["./target/criterion".to_string()],
1674            commands: HashMap::new(),
1675            limitations: PlatformLimitations {
1676                max_job_runtime_minutes: None,
1677                max_artifact_size_mb: None,
1678                max_parallel_jobs: None,
1679                supported_os: vec![
1680                    "linux".to_string(),
1681                    "windows".to_string(),
1682                    "macos".to_string(),
1683                ],
1684            },
1685        }
1686    }
1687}
1688
1689#[cfg(test)]
1690mod tests {
1691    use super::*;
1692
1693    #[test]
1694    fn test_default_config_validation() {
1695        let config = CiCdAutomationConfig::default();
1696        assert!(config.validate().is_ok());
1697    }
1698
1699    #[test]
1700    fn test_invalid_test_execution_config() {
1701        let config = TestExecutionConfig {
1702            test_timeout: 0,
1703            ..Default::default()
1704        };
1705        assert!(config.validate().is_err());
1706
1707        let config = TestExecutionConfig {
1708            test_timeout: 3600,
1709            test_iterations: 0,
1710            ..Default::default()
1711        };
1712        assert!(config.validate().is_err());
1713    }
1714
1715    #[test]
1716    fn test_platform_specific_configs() {
1717        let github_config = PlatformSpecificConfig::github_actions();
1718        assert!(github_config.env_vars.contains_key("CI"));
1719        assert_eq!(github_config.env_vars["CI"], "GITHUB_ACTIONS");
1720
1721        let gitlab_config = PlatformSpecificConfig::gitlab_ci();
1722        assert_eq!(gitlab_config.env_vars["CI"], "GITLAB_CI");
1723    }
1724
1725    #[test]
1726    fn test_metric_gate_validation() {
1727        let gate = MetricGate {
1728            gate_type: GateType::Relative,
1729            threshold: -1.0,
1730            operator: ComparisonOperator::LessThanOrEqual,
1731            severity: GateSeverity::Error,
1732            enabled: true,
1733        };
1734
1735        assert!(gate.validate(&MetricType::ExecutionTime).is_err());
1736    }
1737
1738    #[test]
1739    fn test_platform_enum_serialization() {
1740        let platform = CiCdPlatform::GitHubActions;
1741        let serialized = serde_json::to_string(&platform).expect("unwrap failed");
1742        let deserialized: CiCdPlatform = serde_json::from_str(&serialized).expect("unwrap failed");
1743        assert_eq!(platform, deserialized);
1744    }
1745
1746    #[test]
1747    fn test_comprehensive_config() {
1748        let mut config = CiCdAutomationConfig::default();
1749
1750        // Enable all features
1751        config.reporting.generate_html = true;
1752        config.reporting.generate_json = true;
1753        config.reporting.generate_markdown = true;
1754        config.reporting.generate_pdf = true;
1755        config.artifact_storage.enabled = true;
1756        config.performance_gates.enabled = true;
1757
1758        assert!(config.validate().is_ok());
1759    }
1760}