pmat 3.11.0

PMAT - Zero-config AI context generation and code quality toolkit (CLI, MCP, HTTP)
//! Quality gate configuration management for TICKET-PMAT-5024

use super::gates::GateConfig;

/// Generate default .pmat-gates.toml configuration
///
/// # Complexity
/// - Time: O(1)
/// - Cyclomatic: 1
pub fn generate_default_config() -> String {
    r#"# PMAT Quality Gate Configuration
# Generated by: pmat quality-gates init

[gates]
# Run clippy linter
run_clippy = true

# Enforce strict clippy (-D warnings)
clippy_strict = true

# Run test suite
run_tests = true

# Test timeout in seconds
test_timeout = 300

# Check code coverage
check_coverage = true

# Minimum coverage percentage (0-100)
min_coverage = 80.0

# Check cyclomatic complexity
check_complexity = true

# Maximum cyclomatic complexity per function
max_complexity = 10
"#
    .to_string()
}

/// Validate gate configuration
///
/// # Complexity
/// - Time: O(1)
/// - Cyclomatic: 5
pub fn validate_config(config: &GateConfig) -> Result<(), Vec<String>> {
    let mut errors = Vec::new();

    if config.min_coverage < 0.0 || config.min_coverage > 100.0 {
        errors.push(format!(
            "min_coverage must be 0-100, got {}",
            config.min_coverage
        ));
    }

    if config.test_timeout == 0 {
        errors.push("test_timeout must be > 0".to_string());
    }

    if config.max_complexity == 0 {
        errors.push("max_complexity must be > 0".to_string());
    }

    if errors.is_empty() {
        Ok(())
    } else {
        Err(errors)
    }
}

/// Generate TOML from config struct
///
/// # Complexity
/// - Time: O(1)
/// - Cyclomatic: 1
pub fn generate_config_toml(config: &GateConfig) -> String {
    format!(
        r#"[gates]
run_clippy = {}
clippy_strict = {}
run_tests = {}
test_timeout = {}
check_coverage = {}
min_coverage = {}
check_complexity = {}
max_complexity = {}
"#,
        config.run_clippy,
        config.clippy_strict,
        config.run_tests,
        config.test_timeout,
        config.check_coverage,
        config.min_coverage,
        config.check_complexity,
        config.max_complexity
    )
}

#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_generate_default_config() {
        let config = generate_default_config();

        assert!(config.contains("[gates]"));
        assert!(config.contains("run_clippy = true"));
        assert!(config.contains("min_coverage = 80.0"));
        assert!(config.contains("max_complexity = 10"));
        assert!(config.contains("# PMAT Quality Gate Configuration"));
    }

    #[test]
    fn test_validate_valid_config() {
        let config = GateConfig::default();
        assert!(validate_config(&config).is_ok());
    }

    #[test]
    #[allow(clippy::field_reassign_with_default)]
    fn test_validate_invalid_coverage_too_high() {
        let mut config = GateConfig::default();
        config.min_coverage = 150.0;

        let result = validate_config(&config);
        assert!(result.is_err());
        let errors = result.unwrap_err();
        assert_eq!(errors.len(), 1);
        assert!(errors[0].contains("min_coverage"));
        assert!(errors[0].contains("150"));
    }

    #[test]
    #[allow(clippy::field_reassign_with_default)]
    fn test_validate_invalid_coverage_negative() {
        let mut config = GateConfig::default();
        config.min_coverage = -10.0;

        let result = validate_config(&config);
        assert!(result.is_err());
        let errors = result.unwrap_err();
        assert!(errors[0].contains("min_coverage"));
    }

    #[test]
    fn test_validate_zero_timeout() {
        let mut config = GateConfig::default();
        config.test_timeout = 0;

        let result = validate_config(&config);
        assert!(result.is_err());
        let errors = result.unwrap_err();
        assert!(errors.iter().any(|e| e.contains("test_timeout")));
    }

    #[test]
    fn test_validate_zero_complexity() {
        let mut config = GateConfig::default();
        config.max_complexity = 0;

        let result = validate_config(&config);
        assert!(result.is_err());
        let errors = result.unwrap_err();
        assert!(errors.iter().any(|e| e.contains("max_complexity")));
    }

    #[test]
    fn test_validate_multiple_errors() {
        let config = GateConfig {
            run_clippy: true,
            clippy_strict: true,
            run_tests: true,
            test_timeout: 0,
            check_coverage: true,
            min_coverage: 150.0,
            check_complexity: true,
            max_complexity: 0,
        };

        let result = validate_config(&config);
        assert!(result.is_err());
        let errors = result.unwrap_err();
        assert_eq!(errors.len(), 3); // All three validations should fail
    }

    #[test]
    fn test_generate_config_toml() {
        let config = GateConfig {
            run_clippy: true,
            clippy_strict: false,
            run_tests: true,
            test_timeout: 120,
            check_coverage: true,
            min_coverage: 85.0,
            check_complexity: true,
            max_complexity: 8,
        };

        let toml = generate_config_toml(&config);

        assert!(toml.contains("[gates]"));
        assert!(toml.contains("run_clippy = true"));
        assert!(toml.contains("clippy_strict = false"));
        assert!(toml.contains("test_timeout = 120"));
        assert!(toml.contains("min_coverage = 85"));
        assert!(toml.contains("max_complexity = 8"));
    }

    #[test]
    fn test_generate_config_toml_all_disabled() {
        let config = GateConfig {
            run_clippy: false,
            clippy_strict: false,
            run_tests: false,
            test_timeout: 60,
            check_coverage: false,
            min_coverage: 50.0,
            check_complexity: false,
            max_complexity: 20,
        };

        let toml = generate_config_toml(&config);

        assert!(toml.contains("run_clippy = false"));
        assert!(toml.contains("run_tests = false"));
        assert!(toml.contains("check_coverage = false"));
        assert!(toml.contains("check_complexity = false"));
    }
}